要求:根据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<<"移动右眼"<
//覆盖原图
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<<"移动嘴巴"<
//覆盖原图
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
//cout<<"移动鼻子"<
//覆盖原图
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
//利用背景黑色覆盖皇冠
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)