OpenGL初探:二维卡通人物交互设计

来源:互联网 发布:铆工放样软件 编辑:程序博客网 时间:2024/04/19 18:21

使用OpenGL实现基于鼠标交互的卡通人物和其它环境物体的设计与绘制。使用颜色填充与反走样技术对卡通人物外貌以及衣着进行绘制。实现对卡通人物或物体轮廓的交互控制,点击鼠标左键可以对人物或者物体进行拖拽移动调整。按“↑”按键能够实现卡通人物绕坐标原点(或指定点)进行旋转,按“z”键可实现对选中的人物或者物体进行放缩。选中其中的一个多边形区域,点击鼠标右键,弹出一个菜单,可以对该区域进行不同颜色的选择。


首先设计图形,选择简单的卡通人物可以简化设计过程、加快渲染速度。这里选择机器猫作为绘制的对象,并对其形象进行简化,通过绘制圆、半圆、椭圆、线等简单图形,组合成一个整体的人物形象:

//绘制身体void DrawBody(int x, int y) {/*glEnable(GL_BLEND);//启用颜色混合glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);//接受源颜色并将这个颜色(RGB)与alpha值相乘,  //然后把这个结果加上目标颜色乘“1减去源颜色的alpha值”的结果*///启用防走样glEnable(GL_BLEND);glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);glHint(GL_POLYGON_SMOOTH_HINT, GL_FASTEST);glEnable(GL_POINT_SMOOTH);glEnable(GL_LINE_SMOOTH);glEnable(GL_POLYGON_SMOOTH);//左胳膊{glLoadIdentity();glRotatef(theta, 0.0, 0.0, 1.0);glTranslatef(-38.0, 10.0, 0.0);glRotatef(45.0, 0.0, 0.0, 1.0);glColor3f(ColorChoose[BODY_COLOR][0], ColorChoose[BODY_COLOR][1], ColorChoose[BODY_COLOR][2]);glBegin(GL_POLYGON);for (int i = 0; i < N; i++) {tempX = 20 * cos(2 * PI*i / N);tempY = 10 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();//描边glColor3f(ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][1], ColorChoose[LINE_COLOR][2]);glLineWidth(1.0);//线条粗细设置glBegin(GL_LINE_LOOP);for (int i = 0; i < N; i++) {tempX = 20 * cos(2 * PI*i / N);tempY = 10 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();}//左手{glLoadIdentity();glRotatef(theta, 0.0, 0.0, 1.0);glTranslatef(-50.0, 0.0, 0.0);glColor3f(ColorChoose[HAND_COLOR][0], ColorChoose[HAND_COLOR][1], ColorChoose[HAND_COLOR][2]);glBegin(GL_POLYGON);for (int i = 0; i < N; i++) {tempX = 8 * cos(2 * PI*i / N);tempY = 8 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();glColor3f(ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][1], ColorChoose[LINE_COLOR][2]);glLineWidth(1.0);//线条粗细设置glBegin(GL_LINE_LOOP);for (int i = 0; i < N; i++) {tempX = 8 * cos(2 * PI*i / N);tempY = 8 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();}//右胳膊{glLoadIdentity();glRotatef(theta, 0.0, 0.0, 1.0);glTranslatef(38.0, 10.0, 0.0);glRotatef(-45.0, 0.0, 0.0, 1.0);glColor3f(ColorChoose[BODY_COLOR][0], ColorChoose[BODY_COLOR][1], ColorChoose[BODY_COLOR][2]);glBegin(GL_POLYGON);for (int i = 0; i < N; i++) {tempX = 20 * cos(2 * PI*i / N);tempY = 10 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();glColor3f(ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][1], ColorChoose[LINE_COLOR][2]);glLineWidth(1.0);//线条粗细设置glBegin(GL_LINE_LOOP);for (int i = 0; i < N; i++) {tempX = 20 * cos(2 * PI*i / N);tempY = 10 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();}//右手{glLoadIdentity();glRotatef(theta, 0.0, 0.0, 1.0);glTranslatef(50.0, 0.0, 0.0);glColor3f(ColorChoose[HAND_COLOR][0], ColorChoose[HAND_COLOR][1], ColorChoose[HAND_COLOR][2]);glBegin(GL_POLYGON);for (int i = 0; i < N; i++) {tempX = 8 * cos(2 * PI*i / N);tempY = 8 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();glColor3f(ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][1], ColorChoose[LINE_COLOR][2]);glLineWidth(1.0);//线条粗细设置glBegin(GL_LINE_LOOP);for (int i = 0; i < N; i++) {tempX = 8 * cos(2 * PI*i / N);tempY = 8 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();}//身体{glLoadIdentity();glRotatef(theta, 0.0, 0.0, 1.0);glColor3f(ColorChoose[BODY_COLOR][0], ColorChoose[BODY_COLOR][1], ColorChoose[BODY_COLOR][2]);glBegin(GL_POLYGON);for (int i = 0; i <= N / 8; i++) {tempX = 40 * cos(2 * PI*i / N);tempY = 42 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}for (int i = 3 * N / 8; i <= 5 * N / 8; i++) {tempX = 40 * cos(2 * PI*i / N);tempY = 42 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}for (int i = 7 * N / 8; i < N; i++) {tempX = 40 * cos(2 * PI*i / N);tempY = 42 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();//描边glColor3f(ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][0]);glLineWidth(1.0);//线条粗细设置glBegin(GL_LINE_LOOP);for (int i = 0; i <= N / 8; i++) {tempX = 40 * cos(2 * PI*i / N);tempY = 42 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}for (int i = 3 * N / 8; i <= 5 * N / 8; i++) {tempX = 40 * cos(2 * PI*i / N);tempY = 42 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}for (int i = 7 * N / 8; i < N; i++) {tempX = 40 * cos(2 * PI*i / N);tempY = 42 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();}//项圈{glLoadIdentity();glRotatef(theta, 0.0, 0.0, 1.0);glColor3f(ColorChoose[NECKLACE_COLOR][0], ColorChoose[NECKLACE_COLOR][1], ColorChoose[NECKLACE_COLOR][2]);glBegin(GL_POLYGON);tempX = 40 * cos(2 * PI * 5 / N);tempY = 42 * sin(2 * PI * 5 / N);glVertex2f(tempX, tempY);tempX = 40 * cos(2 * PI * 15 / N);//tempY= 40 * sin(2 * PI*15 / N);glVertex2f(tempX, tempY);tempY = 40 * sin(2 * PI * 15 / N) + 5;glVertex2f(tempX, tempY);tempX = 40 * cos(2 * PI * 5 / N);glVertex2f(tempX, tempY);glEnd();glColor3f(ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][0]);glLineWidth(1.0);//线条粗细设置glBegin(GL_LINE_LOOP);tempX = 40 * cos(2 * PI * 5 / N);tempY = 42 * sin(2 * PI * 5 / N);glVertex2f(tempX, tempY);tempX = 40 * cos(2 * PI * 15 / N);//tempY= 40 * sin(2 * PI*15 / N);glVertex2f(tempX, tempY);tempY = 40 * sin(2 * PI * 15 / N) + 5;glVertex2f(tempX, tempY);tempX = 40 * cos(2 * PI * 5 / N);glVertex2f(tempX, tempY);glEnd();}//头部{translate = 42 * sin(2 * PI * 5 / N) + 40 * sin(2 * PI * 5 / N) + 4;glLoadIdentity();glRotatef(theta, 0.0, 0.0, 1.0);glTranslatef(0.0, translate, 0.0);glColor3f(ColorChoose[BODY_COLOR][0], ColorChoose[BODY_COLOR][1], ColorChoose[BODY_COLOR][2]);glBegin(GL_POLYGON);for (int i = 0; i <= 5 * N / 8; i++) {tempX = 40 * cos(2 * PI*i / N);tempY = 40 * sin(2 * PI*i / N) ;glVertex2f(tempX, tempY);}for (int i = 7 * N / 8; i < N; i++) {tempX = 40 * cos(2 * PI*i / N) ;tempY = 40 * sin(2 * PI*i / N) ;glVertex2f(tempX, tempY);}glEnd();glColor3f(ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][0]);glLineWidth(1.0);//线条粗细设置glBegin(GL_LINE_LOOP);for (int i = 0; i <= 5 * N / 8; i++) {tempX = 40 * cos(2 * PI*i / N);tempY = 40 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}for (int i = 7 * N / 8; i < N; i++) {tempX = 40 * cos(2 * PI*i / N);tempY = 40 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();}//脸{tempR = 40 * cos(2 * PI * 5 / N) / cos(35.0*PI/180.0);//脸的半径translate = 42 * sin(2 * PI * 5 / N) + tempR*sin(35.0*PI / 180.0) + 5;glLoadIdentity();glRotatef(theta, 0.0, 0.0, 1.0);glTranslatef(0.0, translate, 0.0);glColor3f(ColorChoose[FACE_COLOR][0], ColorChoose[FACE_COLOR][1], ColorChoose[FACE_COLOR][2]);glBegin(GL_POLYGON);for (int i = 0; i < 7 * N / 12 + 2; i++) {tempX = tempR*cos(2 * PI*i / N);tempY = tempR*sin(2 * PI*i / N);glVertex2f(tempX, tempY);}for (int i = 11 * N / 12; i < N; i++) {tempX = tempR*cos(2 * PI*i / N);tempY = tempR*sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();}//肚子{tempR = 40 * cos(2 * PI * 5 / N) / cos(35.0*PI / 180.0)-5;//肚子的半径translate = 42 * sin(2 * PI * 5 / N) - tempR*sin(35.0*PI / 180.0)-1;glLoadIdentity();glRotatef(theta, 0.0, 0.0, 1.0);glTranslatef(0.0, translate, 0.0);glColor3f(ColorChoose[FACE_COLOR][0], ColorChoose[FACE_COLOR][1], ColorChoose[FACE_COLOR][2]);glBegin(GL_POLYGON);for (int i = 0; i < N / 12 + 2; i++) {tempX = tempR*cos(2 * PI*i / N);tempY = tempR*sin(2 * PI*i / N);glVertex2f(tempX, tempY);}for (int i = 5 * N / 12; i < N; i++) {tempX = tempR*cos(2 * PI*i / N);tempY = tempR*sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();//口袋tempR = 18;glBegin(GL_POLYGON);for (int i = N / 2; i <= N; i++) {tempX = tempR * cos(2 * PI*i / N);tempY = tempR * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();glColor3f(ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][0]);glLineWidth(1.0);//线条粗细设置glBegin(GL_LINE_LOOP);for (int i = N / 2; i <= N; i++) {tempX = tempR * cos(2 * PI*i / N);tempY = tempR * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();}//左眼{tempR = 40 * cos(2 * PI * 5 / N) / cos(35.0*PI / 180.0);//脸的半径translate = 42 * sin(2 * PI * 5 / N) + tempR*sin(35.0*PI / 180.0) + 5 + tempR;glLoadIdentity();glRotatef(theta, 0.0, 0.0, 1.0);glTranslatef(-6.0, translate, 0.0);glColor3f(ColorChoose[EYE_OUT_COLOR][0], ColorChoose[EYE_OUT_COLOR][1], ColorChoose[EYE_OUT_COLOR][2]);glBegin(GL_POLYGON);for (int i = 0; i < N; i++) {tempX = 6*cos(2 * PI*i / N);tempY = 8*sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();glColor3f(ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][0]);glLineWidth(1.0);//线条粗细设置glBegin(GL_LINE_LOOP);for (int i = 0; i < N; i++) {tempX = 6*cos(2 * PI*i / N);tempY = 8*sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();}//左眼眼珠{tempR = 40 * cos(2 * PI * 5 / N) / cos(35.0*PI / 180.0);//脸的半径translate = 42 * sin(2 * PI * 5 / N) + tempR*sin(35.0*PI / 180.0) + 5 + tempR;glLoadIdentity();glRotatef(theta, 0.0, 0.0, 1.0);glTranslatef(-3.0, translate, 0.0);glColor3f(ColorChoose[EYE_IN_COLOR][0], ColorChoose[EYE_IN_COLOR][1], ColorChoose[EYE_IN_COLOR][2]);glBegin(GL_POLYGON);for (int i = 0; i < N; i++) {tempX = 3 * cos(2 * PI*i / N);tempY = 3 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();}//右眼{tempR = 40 * cos(2 * PI * 5 / N) / cos(35.0*PI / 180.0);//脸的半径translate = 42 * sin(2 * PI * 5 / N) + tempR*sin(35.0*PI / 180.0) + 5 + tempR;glLoadIdentity();glRotatef(theta, 0.0, 0.0, 1.0);glTranslatef(6.0, translate, 0.0);glColor3f(ColorChoose[EYE_OUT_COLOR][0], ColorChoose[EYE_OUT_COLOR][1], ColorChoose[EYE_OUT_COLOR][2]);glBegin(GL_POLYGON);for (int i = 0; i < N; i++) {tempX = 6 * cos(2 * PI*i / N);tempY = 8 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();glColor3f(ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][0]);glLineWidth(1.0);//线条粗细设置glBegin(GL_LINE_LOOP);for (int i = 0; i < N; i++) {tempX = 6 * cos(2 * PI*i / N);tempY = 8 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();}//右眼眼珠{tempR = 40 * cos(2 * PI * 5 / N) / cos(35.0*PI / 180.0);//脸的半径translate = 42 * sin(2 * PI * 5 / N) + tempR*sin(35.0*PI / 180.0) + 5 + tempR;glLoadIdentity();glRotatef(theta, 0.0, 0.0, 1.0);glTranslatef(3.0, translate, 0.0);glColor3f(ColorChoose[EYE_IN_COLOR][0], ColorChoose[EYE_IN_COLOR][1], ColorChoose[EYE_IN_COLOR][2]);glBegin(GL_POLYGON);for (int i = 0; i < N; i++) {tempX = 3 * cos(2 * PI*i / N);tempY = 3 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();}//鼻子{tempR = 40 * cos(2 * PI * 5 / N) / cos(35.0*PI / 180.0);//脸的半径translate = 42 * sin(2 * PI * 5 / N) + tempR*sin(35.0*PI / 180.0) + tempR-5;glLoadIdentity();glRotatef(theta, 0.0, 0.0, 1.0);glTranslatef(0.0, translate, 0.0);glColor3f(ColorChoose[NOSE_COLOR][0], ColorChoose[NOSE_COLOR][1], ColorChoose[NOSE_COLOR][2]);glBegin(GL_POLYGON);for (int i = 0; i < N; i++) {tempX = 3 * cos(2 * PI*i / N);tempY = 3 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();}//嘴{tempR = 40 * cos(2 * PI * 5 / N) / cos(35.0*PI / 180.0);//脸的半径translate = 42 * sin(2 * PI * 5 / N) + tempR*sin(35.0*PI / 180.0) + tempR - 25;glLoadIdentity();glRotatef(theta, 0.0, 0.0, 1.0);glTranslatef(0.0, translate, 0.0);glColor3f(ColorChoose[NOSE_COLOR][0], ColorChoose[NOSE_COLOR][1], ColorChoose[NOSE_COLOR][2]);glBegin(GL_POLYGON);for (int i = N/2; i <= N; i++) {tempX = 20 * cos(2 * PI*i / N);tempY = 20 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();glColor3f(ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][0]);glLineWidth(1.0);//线条粗细设置glBegin(GL_LINE_LOOP);for (int i = N / 2 ; i <= N; i++) {tempX = 20 * cos(2 * PI*i / N);tempY = 20 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();glBegin(GL_LINES);glVertex2f(0.0f, 0.0f);glVertex2f(0.0f, 17.0f);glEnd();}//胡须{tempR = 40 * cos(2 * PI * 5 / N) / cos(35.0*PI / 180.0);//脸的半径translate = 42 * sin(2 * PI * 5 / N) + tempR*sin(35.0*PI / 180.0) + tempR - 15;glLoadIdentity();glRotatef(theta, 0.0, 0.0, 1.0);glTranslatef(0.0, translate, 0.0);glColor3f(ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][1], ColorChoose[LINE_COLOR][2]);glBegin(GL_LINES);glVertex2f(-25.0f, 0.0f);glVertex2f(-5.0f, 0.0f);glVertex2f(25.0f, 0.0f);glVertex2f(5.0f, 0.0f);glVertex2f(-23.0f, 8.0f);glVertex2f(-5.0f, 4.0f);glVertex2f(23.0f, 8.0f);glVertex2f(5.0f, 4.0f);glVertex2f(-23.0f, -8.0f);glVertex2f(-5.0f, -4.0f);glVertex2f(23.0f, -8.0f);glVertex2f(5.0f, -4.0f);glEnd();}//左脚{tempR = 20 * cos(2 * PI * 5 / N);translate = 42 * sin(2 * PI * 5 / N);glLoadIdentity();glRotatef(theta, 0.0, 0.0, 1.0);glTranslatef(-tempR, -translate, 0.0);glColor3f(ColorChoose[FOOT_COLOR][0], ColorChoose[FOOT_COLOR][1], ColorChoose[FOOT_COLOR][2]);glBegin(GL_POLYGON);for (int i = 0; i < N; i++) {tempX = tempR * cos(2 * PI*i / N);tempY = tempR * 0.5 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();glColor3f(ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][0]);glLineWidth(1.0);//线条粗细设置glBegin(GL_LINE_LOOP);for (int i = 0; i < N; i++) {tempX = tempR * cos(2 * PI*i / N);tempY = tempR * 0.5 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();}//右脚{tempR = 20 * cos(2 * PI * 5 / N);translate = 42 * sin(2 * PI * 5 / N);glLoadIdentity();glRotatef(theta, 0.0, 0.0, 1.0);glTranslatef(tempR, -translate, 0.0);glColor3f(ColorChoose[FOOT_COLOR][0], ColorChoose[FOOT_COLOR][1], ColorChoose[FOOT_COLOR][2]);glBegin(GL_POLYGON);for (int i = 0; i < N; i++) {tempX = tempR * cos(2 * PI*i / N);tempY = tempR * 0.5 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();glColor3f(ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][1], ColorChoose[LINE_COLOR][2]);glLineWidth(1.0);//线条粗细设置glBegin(GL_LINE_LOOP);for (int i = 0; i < N; i++) {tempX = tempR * cos(2 * PI*i / N);tempY = tempR * 0.5 * sin(2 * PI*i / N);glVertex2f(tempX, tempY);}glEnd();}}

以上是主体人物的绘制,接下来绘制一些小物件:

//绘制帽子void DrawHat(int x, int y) {glLoadIdentity();//若是穿在身上,则一起旋转if (HAT_WEARING == TRUE)glRotatef(theta, 0.0, 0.0, 1.0);//若是选择缩放的状态,将图形移动到原点,缩放后再移动到原位置if (SCALE_STATE == TRUE && SelectPart == HAT) {glTranslatef(x, y, 0.0);glScalef(scale, scale, 1);glTranslatef(-x, -y, 0.0);}else {}glColor3f(ColorChoose[HAT_COLOR][0], ColorChoose[HAT_COLOR][1], ColorChoose[HAT_COLOR][2]);//将物体z坐标置为1,表示层级在身体之上tempR = 18;glBegin(GL_POLYGON);for (int i = 0; i <= N/2; i++) {tempX = tempR * cos(2 * PI*i / N);tempY = tempR * sin(2 * PI*i / N);glVertex3f(tempX+x, tempY+y,1.5);}glEnd();glColor3f(ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][1], ColorChoose[LINE_COLOR][2]);glLineWidth(1.0);//线条粗细设置glBegin(GL_LINE_LOOP);for (int i = 0; i <= N/2; i++) {tempX = tempR * cos(2 * PI*i / N);tempY = tempR * sin(2 * PI*i / N);glVertex3f(tempX+x, tempY+y,1.5);}glEnd();glColor3f(ColorChoose[HAT_COLOR][0], ColorChoose[HAT_COLOR][1], ColorChoose[HAT_COLOR][2]);tempR = 5;glBegin(GL_POLYGON);for (int i = 0; i <= N; i++) {tempX = tempR * cos(2 * PI*i / N);tempY = tempR * sin(2 * PI*i / N) + 23;glVertex3f(tempX + x, tempY + y, 1.5);}glEnd();glColor3f(ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][1], ColorChoose[LINE_COLOR][2]);glLineWidth(1.0);//线条粗细设置glBegin(GL_LINE_LOOP);for (int i = 0; i <= N; i++) {tempX = tempR * cos(2 * PI*i / N);tempY = tempR * sin(2 * PI*i / N) + 23;glVertex3f(tempX + x, tempY + y, 1.5);}glEnd();glColor3f(ColorChoose[HAT_COLOR][0], ColorChoose[HAT_COLOR][1], ColorChoose[HAT_COLOR][2]);glBegin(GL_POLYGON);glVertex3f(-18.0 + x, 0.0+ y , 1.5);glVertex3f(-18.0 + x, -5.0 + y, 1.5);glVertex3f(18.0 + x, -5.0 + y, 1.5);glVertex3f(18.0 + x, 0.0 + y, 1.5);glEnd();glColor3f(ColorChoose[LINE_COLOR][0], ColorChoose[LINE_COLOR][1], ColorChoose[LINE_COLOR][2]);glLineWidth(1.0);//线条粗细设置glBegin(GL_LINE_LOOP);glVertex3f(-18.0 + x, 0.0 + y, 1.5);glVertex3f(-18.0 + x, -5.0 + y, 1.5);glVertex3f(18.0 + x, -5.0 + y, 1.5);glVertex3f(18.0 + x, 0.0 + y, 1.5);glEnd();}

进行简单的图形绘制后(绘制方法可能比较笨,但是比较好理解),为了增加交互功能,使用户可以使用鼠标进行一系列的交互操作,需要增加图元识别的功能:


首先设置缓存,存放鼠标点击时鼠标所在点的图元数据,包括当前击中图元的数量,各个图元中各个顶点中深度最大和深度最小的顶点的深度;当两个或多个图元重叠时,根据逻辑应将上层图元作为选中图元,因此根据深度数据,选择深度最小的图元,即为浮在最上面的图元,将其状态改为选择状态,表示当前鼠标点击选中该图元,以便进行后续操作。(OpenGL超级宝典第4版P298有相关介绍)

//处理选择,鼠标左键单击时触发#define BUFFER_LENGTH 64//定义缓冲区大小void ChooseProcess(int x, int y) {GLfloat Times;//点击计数器和视口存储GLint hits, viewport[4];//选择缓冲区空间static GLuint SelectBuff[BUFFER_LENGTH];//设置选择缓冲区glSelectBuffer(BUFFER_LENGTH, SelectBuff);//获得视口(0,0,800,600)起始点坐标和视口大小glGetIntegerv(GL_VIEWPORT, viewport);/*for (int i = 0; i < 4; i++) {printf("%d \n", viewport[i]);}*///切换到投影模式,并保存矩阵glMatrixMode(GL_PROJECTION);glPushMatrix();//修改渲染模式,改为选择模式glRenderMode(GL_SELECT);//围绕鼠标光标点(x,y)建立新的单位立方体裁剪区域,并在垂直和水平方向扩展2个像素glLoadIdentity();gluPickMatrix(x, viewport[3] - y, 2, 2, viewport);//根据视口大小调整裁切区域的大小Times = (float)viewport[2] / (float)viewport[3];if (viewport[2] <= viewport[3]) {glOrtho(-VIEWSIZE, VIEWSIZE, -VIEWSIZE * Times, VIEWSIZE * Times, -VIEWSIZE, VIEWSIZE);}else {glOrtho(-VIEWSIZE * Times, VIEWSIZE * Times, -VIEWSIZE, VIEWSIZE, -VIEWSIZE, VIEWSIZE);}//绘制场景myDisplay();//收集点击记录hits = glRenderMode(GL_RENDER);//printf("1> Hits is %d\n", hits);//缓存器中的数据4个为一组,分别记录着//名字堆栈的数量,//可视区域内包含的所有顶点的最小和最大的z坐标,范围是[0-1],但会被扩充到无符号整数的最大长度//名字堆栈的底部if (hits > 0) {int Choose = SelectBuff[3];int depth = SelectBuff[1];//printf("2> SelectBuffer[3] is %d\n", Choose);//printf("2> SelectBuffer[1] is %d\n", depth);//遍历缓存中的数据,选择深度较小的图元for (int loop = 1; loop < hits; loop++) {if (SelectBuff[loop * 4 + 1] < GLuint(depth)) {Choose = SelectBuff[loop * 4 + 3];depth = SelectBuff[loop * 4 + 1];}//printf(">3 Choose is %d depth is  %d \n", Choose, depth);}//printf(">3 Hits is %d Choose is %d \n", hits, Choose);ChooseState(GLuint(Choose));//根据选择的图元,改变它的状态}//点击数为0,说明击中的是空白部分,重置所有状态if (hits == 0) {ChooseState(0);}/*printf("\n show buffer\n");for (int i = 0; i < BUFFER_LENGTH; i++) {printf("SelectBuffer[%d] is %d  ", i, SelectBuff[i]);if (i % 5 == 0)printf("\n");}printf("\n");*///恢复投影矩阵glMatrixMode(GL_PROJECTION);glPopMatrix();//恢复到模型视图glMatrixMode(GL_MODELVIEW);}

接下来编写各类回调函数:

首先添加窗口大小改变时的回调函数ChangeSize,当用户改变窗口的大小时,为了使图形仍然保持正确的比例,需要对窗口进行重新裁剪:

//窗口大小改变时调用的函数void ChangeSize(int w, int h) {GLfloat Times;if (h == 0)h = 1;glViewport(0, 0, w, h);Times = (GLfloat)w / (GLfloat)h;glMatrixMode(GL_PROJECTION);glLoadIdentity();//根据窗口大小调整裁切区域的大小if (w <= h) {glOrtho(-VIEWSIZE, VIEWSIZE, -VIEWSIZE / Times, VIEWSIZE / Times, -VIEWSIZE, VIEWSIZE);}else {glOrtho(-VIEWSIZE * Times, VIEWSIZE * Times, -VIEWSIZE, VIEWSIZE, -VIEWSIZE, VIEWSIZE);}printf("WindowSize changed , now VIEWSIZE is %d \n", VIEWSIZE);glMatrixMode(GL_MODELVIEW);glLoadIdentity();glEnable(GL_BLEND);glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);glHint(GL_POLYGON_SMOOTH_HINT, GL_FASTEST);glEnable(GL_POLYGON_SMOOTH);glEnable(GL_POINT_SMOOTH);glEnable(GL_LINE_SMOOTH);}

接下来编写鼠标的回调函数:

当用户使用鼠标点击时,调用之前关于图元识别的函数,确定用户是否选中某图元,并对图元的状态进行修改,以便后续绘制时进行相应的变换。

以帽子为例,如果选中的图元是帽子(即HAT_STATE变量为TRUE),则将HAT_WEARING变量设置为TRUE,表示它被穿着在人物身上,在绘制时,可以随着人物一起转动。这里的判定是为了将帽子摆正,当用户的鼠标在一定范围内松开时,将帽子自动对齐到中央位置:

//帽子位置判断if (button == GLUT_LEFT_BUTTON&&state == GLUT_UP&&HAT_STATE == TRUE) {//printf("Loop1 success!");if (xnow > -15 && xnow < 15 && ynow>103 && ynow < 115) {HAT_WEARING = TRUE;}else {HAT_WEARING = FALSE;}glutPostRedisplay();myDisplay();}

接下来是鼠标移动的回调函数,当鼠标移动时触发。

因为系统返回的是鼠标的窗口坐标,并不是世界坐标,因此为了使图像和鼠标同步运动,需要进行坐标的转换:

if (HAT_STATE == TRUE){if (viewport[2] <= viewport[3]){Hat[0] = -VIEWSIZE + 2 * VIEWSIZE * x / viewport[2];Hat[1] = -VIEWSIZE * Times + (viewport[3] - y) * 2 * VIEWSIZE * Times / viewport[3];}else{Hat[0] = -VIEWSIZE * Times + 2 * VIEWSIZE * Times*x / viewport[2];Hat[1] = -VIEWSIZE + (viewport[3] - y) * 2 * VIEWSIZE / viewport[3];}//printf("Now Mouse is on ( %d , %d ) \n", Crown[0], Crown[1]);//移动到指定位置,改变状态为wearingif (Hat[0] > -15 && Hat[0] < 15 && Hat[1]>103 && Hat[1] < 115) {HAT_WEARING = TRUE;}else {HAT_WEARING = FALSE;}myDisplay();}

接下来是键盘响应函数,当用户点击键盘时,触发对应的功能:

当用户点击z时,要实现缩放的功能,因此设置一个scale变量,它的值表示缩放的倍数,并且在1、2、3之间变换,每当用户点击时,就进行切换,而scale变量在绘制图形的函数中作为glScalef函数的参数,实现图形缩放的功能;当用户点击↑键时,则更改变量theta的值,它代表旋转的角度,同样在绘制函数中作为glRotatef函数的参数使用,完成图形旋转功能,前文中提到图元可以设置是否穿戴,只有穿戴在卡通人物身上的图元会随着人物旋转,而旁边没有穿戴的图元则不进行旋转:

//键盘响应函数void myKeyboard(unsigned char key, int x, int y) {switch (key){case 'z':SCALE_STATE = TRUE;scale += 1;if (scale == 4)scale = 1;glutPostRedisplay();myDisplay();//SCALE_STATE = FALSE;break;default:break;}}//键盘方向键响应,根据按下的方向键进行操作void mySpecial(int key, int x, int y) {switch (key){case GLUT_KEY_UP:theta = (theta + 10);glutPostRedisplay();myDisplay();break;case GLUT_KEY_DOWN:theta = (theta - 10);glutPostRedisplay();myDisplay();break;default:break;}}

菜单功能的实现:

使用glutCreateMenu()函数创建菜单,使用glutAttachMenu(GLUT_RIGHT_BUTTON)函数将菜单绑定到鼠标右键上,这样当用户点击右键时,菜单即可呼出,使用glutAddMenuEntry()函数设置菜单项,第一个参数是菜单项显示的文字,第二项是传递给判断函数menu的值:

//创建右键菜单glutCreateMenu(myMenu);glutAddMenuEntry("balck", 0);//第一个参数是菜单项显示的文本,第二个参数是传递给menu函数的值glutAddMenuEntry("red", 1);glutAddMenuEntry("green", 2);glutAddMenuEntry("blue", 3);glutAddMenuEntry("cyan", 4);glutAddMenuEntry("purple", 5);glutAddMenuEntry("yellow", 6);glutAddMenuEntry("white", 7);glutAttachMenu(GLUT_RIGHT_BUTTON);

其中判断函数menu的功能是根据传入的参数,进行相应的操作:

//菜单void myMenu(int index) {//根据选项设定当前选中图元的颜色switch (SelectPart){case CROWN:CROWN_COLOR = index;break;case HAT:HAT_COLOR = index;break;case FLAG:FLAG_COLOR = index;break;default:break;}//二次绘制防闪烁glutPostRedisplay();myDisplay();}


程序运行结果:


选中部件可以进行拖动:


穿戴在人物身上的物件可以随人物一同旋转,而未穿戴的物件不旋转:


对选中部件进行缩放(旁边的绿色旗子,因为背景和旗杆都是黑色所以可能看不出来是个旗子):


右键可以将旗子换色: