交互式卡通人物

计算机图形学实验一,参考了汪海学长的逻辑结构 吐舌头 吐舌头 膜拜大佬,抱拳!
觉得学到的最有意思的就是OpenGL的拾取模式,各个模式之间的切换并且互不打扰很厉害。 看到的讲的比较清楚的文章: 链接1链接2


#include "stdafx.h"
#include <Windows.h>  
#include <gl/glut.h>  
#include <stdio.h>    
#include <math.h>  

#define PI 3.14  
#define SIZE 512    

#define BODY 1
#define EYES 2 
#define NOSE 3 
#define MOUTH 4  
#define SKIMMER 5

static bool EYE_STATE = FALSE;
static bool NOSE_STATE = FALSE;
static bool MOUTH_STATE = FALSE;
static bool SKIMMER_STATE = FALSE;

//左眼和鼻子的拖动会用到
static float eye[2] = { -0.12, 0.9 };
static float nose[2] = { 0.0, 0.7 };
static float skimmer[2] = { 1.6f, 0.7f };

static int select_part = -1;

static int NOSE_COLOR = 1;
static int MOUTH_COLOR = 0;
static int EYES_COLOR = 7;

static GLfloat theta = 0;
static GLfloat t_x = 0;
static GLfloat t_y = 0;


static int o_x = 0;
static int o_y = 0;

//这里为什么这样修改?
#define VIEW_WIDTH  4
#define VIEW_HEIGHT  4
#define WIN_WIDTH 500  
#define WIN_HEIGHT 500  

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 } };

//画椭圆
void drawEllipseCircle(float dx, float dy, GLfloat r1, GLfloat r2, int num){
	glBegin(GL_TRIANGLE_FAN);

	for (int i = 1; i <= num; i++)
	{
		glVertex2f(r1*cos(2 * PI / i*num) + dx, r2*sin(2 * PI / i*num) + dy);

	}
	glEnd();
}
//椭圆描线
void drawLineCircle(float dx, float dy, GLfloat r1, GLfloat r2, int num){
	glLineWidth(2);
	glColor3f(0.0f, 0.0f, 0.0f);
	glBegin(GL_LINE_LOOP);
	for (int i = 0; i<num; i++)
		glVertex2f(dx + r1*cos(2 * PI / num*i), dy + r2*sin(2 * PI / num*i));
	glEnd();
}
//画圆弧
void drawCirLine(float lx,float ly,float bx,float ex,float base){
	GLfloat x = -1.0;
	glBegin(GL_LINE_STRIP);
	glColor3f(0.0f, 0.0f, 0.0f);
	for (float x = bx*PI; x < ex*PI; x += 0.1f){
		glVertex2f(lx + x / (base*PI), ly + sin(x));
	}
	glEnd();
}

/*****************画五官*****************/
//画眼睛
void drawEye(){
	//左眼
	glPopMatrix();
	//glColor3f(1.0f, 1.0f, 1.0f);
	drawEllipseCircle(eye[0], eye[1], 0.12, 0.17, 50);
	drawLineCircle(eye[0], eye[1], 0.12, 0.17, 50);
	//左眼珠
	glColor3f(0.0f, 0.0f, 0.0f);
	drawEllipseCircle(eye[0], eye[1], 0.04, 0.05, 50);

	//右眼
	glColor3f(1.0f, 1.0f, 1.0f);
	drawEllipseCircle(0.12, 0.9, 0.12, 0.17, 50);
	drawLineCircle(0.12, 0.9, 0.12, 0.17, 50);
	//右眼微笑,圆弧	
	drawCirLine(-0.2, -0.05, 0.4, 0.6, 1.6);

}

//画鼻子
void drawNose(){
	//glColor3f(1.0f, 0.0f, 0.0f);
	drawEllipseCircle(nose[0],nose[1], 0.08, 0.08, 50);
	drawLineCircle(nose[0], nose[1], 0.08, 0.08, 50);
	//鼻子下面的线
	glBegin(GL_LINES);
	glColor3f(0.0f, 0.0f, 0.0f);
	glVertex2f(0.0f, 0.63f); 
	glVertex2f(0.0f, 0.3f);
	glEnd();
}

//画嘴
void drawMouth(){
	drawCirLine(0.83, 1.3, -0.7, -0.28, 0.6);
	glPushMatrix();
}

/*************画身体*****************/
//画胳膊
void drawLegs(){
	glPushMatrix();
	glColor3f(0.0f, 0.435f, 0.886f);
	//左臂
	glRotatef(15.0, 0.0, 0.0, 1.0);	
	drawEllipseCircle(-0.5, 0.17, 0.37, 0.13, 50);
	//左手
	glColor3f(1.0f, 1.0f, 1.0f);
	drawEllipseCircle(-0.8, 0.13, 0.13, 0.13, 50);
	drawLineCircle(-0.8, 0.13, 0.13, 0.13, 50);
	//右臂
	glColor3f(0.0f, 0.435f, 0.886f);
	glRotatef(-30.0, 0.0, 0.0, 1.0);
	drawEllipseCircle(0.5, 0.17, 0.37, 0.13, 50);
	//右手
	glColor3f(1.0f, 1.0f, 1.0f);
	drawEllipseCircle(0.8, 0.13, 0.13, 0.13, 50);
	drawLineCircle(0.8, 0.13, 0.13, 0.13, 50);	
}

//画个竹蜻蜓,作为装饰物

void drawSkimmer(){
	glPopMatrix();
	glBegin(GL_LINES);
	glLineWidth(3.5);
	glColor3f(1.0f, 0.960f, 0.0f);
	glVertex2f(skimmer[0],skimmer[1]);
	glVertex2f(skimmer[0], skimmer[1]-0.4f);
	glEnd();
	drawEllipseCircle(skimmer[0]-0.15f, skimmer[1], 0.2f, 0.035f, 50);
	drawEllipseCircle(skimmer[0]+0.15f, skimmer[1], 0.2f, 0.035f,50);
	drawLineCircle(skimmer[0] - 0.15f, skimmer[1], 0.2f, 0.035f, 50);
	drawLineCircle(skimmer[0] + 0.15f, skimmer[1], 0.2f, 0.035f, 50);
}

void drawBody(){
	glEnable(GL_BLEND);             //启用混合功能,将图形颜色同周围颜色相混合    
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_POINT_SMOOTH);       //点抗锯齿    
	glEnable(GL_LINE_SMOOTH);        //线抗锯齿    
	glEnable(GL_POLYGON_SMOOTH);     //多边形抗锯齿  
	glBegin(GL_TRIANGLE_FAN);

	//画头
	int n = 200;
	GLfloat a = 0.62f, b = 0.6f;
	glBegin(GL_TRIANGLE_FAN);
	glColor3f(0.0f, 0.435f, 0.886f);
	for (int i = 1; i <= n; i++)
	{
		glVertex2f(a*cos(2 * PI / i*n), b*sin(2 * PI / i*n)+0.7);

	}
	glEnd();

	//画内头
	glColor3f(1.0f, 1.0f, 1.0f);
	drawEllipseCircle(0, 0.55, 0.47, 0.44, 50);
	//内头描边
	drawLineCircle(0, 0.55, 0.47, 0.44, 50);

	//躯干,长方形
	glBegin(GL_POLYGON);
	glColor3f(0.0f, 0.435f, 0.886f);
	glVertex2f(-0.45, 0.15);
	glVertex2f(0.45, 0.15);
	glVertex2f(0.45, -0.7);
	glVertex2f(-0.45, -0.7);
	glEnd();
	
	//画脚
	//左脚
	glColor3f(1.0f, 1.0f, 1.0f);
	drawEllipseCircle(-0.27, -0.75, 0.27, 0.08, 50);
	//左脚描边
	drawLineCircle(-0.27, -0.75, 0.27, 0.08, 50);
	//左脚
	glColor3f(1.0f, 1.0f, 1.0f);
	drawEllipseCircle(0.27, -0.75, 0.27, 0.08, 50);
	//左脚描边
	drawLineCircle(0.27, -0.75, 0.27, 0.08, 50);

	//肚子
	glColor3f(1.0f, 1.0f, 1.0f);
	drawEllipseCircle(0.02, -0.2, 0.36, 0.35, 50);
	//肚子描边
	drawLineCircle(0.02,-0.2, 0.36, 0.35, 50);
	
	//画领带
	glBegin(GL_POLYGON);
	glColor3f(1.0f, 0.0f, 0.0f);
	glVertex2f(-0.35, 0.2);
	glVertex2f(0.35, 0.2);
	glVertex2f(0.40, 0.1);
	glVertex2f(-0.40, 0.1);
	glEnd();
	
	//画铃铛
	glColor3f(1.0f, 0.960f, 0.0f);
	drawEllipseCircle(0.02, 0.15, 0.07, 0.07, 50);
	//铃铛描边
	drawLineCircle(0.02, 0.15, 0.07, 0.07, 50);

	//画口袋
	glBegin(GL_POLYGON);
	glColor3f(1.0f, 0.960f, 0.0f);
	glVertex2f(-0.25, -0.1);
	glVertex2f(0.25, -0.1);
	glVertex2f(0.15, -0.38);
	glVertex2f(-0.15, -0.38);
	glEnd();

	glBegin(GL_LINES);
	glColor3f(0.0f, 0.0f, 0.0f);
	glVertex2f(0.0f, -0.55f);
	glVertex2f(0.0f, -0.69f);
	glEnd();
	//画胳膊
	drawLegs();	

	//画一条线
	glPopMatrix();
	
	glPushMatrix();
}
void DrawLine(){
	glLoadIdentity();
	glBegin(GL_LINES);
	glLineWidth(2);
	glColor3f(1.0f, 1.0f, 1.0f);
	glVertex2f(1.2f, 4.0f);
	glVertex2f(1.2f, -4.0f);
	glEnd();
}


void drawGraph(GLenum mode){
	if (mode == GL_SELECT)
		glLoadName(BODY);
	//画脸  
	drawBody();

	if (mode == GL_SELECT)
		glLoadName(EYES);
	//画眼睛
	glColor3f(colors[EYES_COLOR][0], colors[EYES_COLOR][1], colors[EYES_COLOR][2]);
	drawEye();	
		
	if (mode == GL_SELECT)
		glLoadName(NOSE);
	//画鼻子 
	glColor3f(colors[NOSE_COLOR][0], colors[NOSE_COLOR][1], colors[NOSE_COLOR][2]);
	drawNose();
			
	if (mode == GL_SELECT)
		glLoadName(MOUTH);
	//画嘴巴
	glColor3f(colors[MOUTH_COLOR][0], colors[MOUTH_COLOR][1], colors[MOUTH_COLOR][2]);
	drawMouth();

	if (mode == GL_SELECT)
		glLoadName(SKIMMER);
	drawSkimmer();

	DrawLine();
}

//指定绘制的永久环境,在开始的时候被调用一次
void myInit(){
	glClearColor(0.0, 0.333, 0.243, 0.0);
}

void myDisplay(){
	//清除缓存  
	glClear(GL_COLOR_BUFFER_BIT);
	//glClearColor(0.0, 0.333, 0.243, 0.0);
	glLoadIdentity();
	glRotatef(theta, 0.0, 0.0, 1.0);//旋转
	//glTranslatef(t_x, t_y, 0.0);//平移

	//RANDER模式绘制物体  
	drawGraph(GL_RENDER);
	//绘制  
	glFlush();
}

//重新定义大小,使得视口坐标系和世界坐标系有相同的缩放比
void myReshape(int w, int h){
	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(-VIEW_WIDTH / 2, VIEW_WIDTH / 2, -VIEW_HEIGHT / 2, VIEW_HEIGHT / 2);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	//反走样
	glEnable(GL_BLEND);//启用颜色混合,例如实现半透明效果
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);//表示把渲染的图像融合到目标区域。也就是说源的每一个像素的alpha都等于自己的alpha,
	//目标的每一个像素的alpha等于1减去该位置源像素的alpha。 因此不论叠加多少次,亮度是不变的。
	glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);//指示是锯消除多边形的采样质量 
	glEnable(GL_POLYGON_SMOOTH);//执行后,过滤多边形的锯齿
	glEnable(GL_POINT_SMOOTH);
	glEnable(GL_LINE_SMOOTH);
}

void processMenuEvents(int index){
	if (index == -1)
		return;

	switch (select_part) {
	case EYES:
		EYES_COLOR = index;
		break;
	case NOSE:
		NOSE_COLOR = index;
		break;
	case MOUTH:
		MOUTH_COLOR = index;
		break;
	}
	glutPostRedisplay();//标记当前窗口需要重新绘制 
}

void processHits(GLint hits, GLuint buffer[])
{
	unsigned int i, j;
	GLuint names, *ptr;
	//printf("一共选中%d个!\n", hits);
	ptr = (GLuint *)buffer;
	for (i = 1; i <= hits; i++) {/*  for each hit  */
		names = *ptr;
		ptr += 3;
		for (j = 0; j < names; j++)
		{ /*  for each name */
			
			if (*ptr == 1){
				//printf("身体\n");
			}
		    else if (*ptr == 2){
				//printf("眼睛\n");
				select_part = EYES;
			}
			else if (*ptr == 3){
				//printf("鼻子\n");
				select_part = NOSE;				
			}
			else if (*ptr == 4){
				//printf("嘴巴\n");
				select_part = MOUTH;
			}
			else if (*ptr == 5){
				//printf("嘴巴\n");
				select_part = SKIMMER;
			}
			ptr++;
		}
	}
	//printf("select_part:%d\n", select_part);
}
static bool left_down = false;
//处理鼠标响应事件
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_DOWN) {
		left_down = true;
		o_x = x;
		o_y = y;
	}

	//左键弹起,拾取矩阵
	if (button == GLUT_LEFT_BUTTON && state == GLUT_UP){
		left_down = false;
		//printf("select_part:%d\n", select_part);
		glGetIntegerv(GL_VIEWPORT, viewport); //获得viewport  
		glSelectBuffer(SIZE, selectBuf); //告诉OpenGL初始化selectbuffer  
		glRenderMode(GL_SELECT); //进入选择模式  

		glInitNames();//初始化名字栈  
		glPushName(0);//在名字栈中放入一个初始化名字,这里为‘0’  

		glMatrixMode(GL_PROJECTION);//进入投影阶段准备拾取  
		glPushMatrix();//保存以前的投影矩阵  
		glLoadIdentity();//载入单位矩阵 
		gluPickMatrix((GLdouble)x, (GLdouble)(viewport[3] - y), 3.0, 3.0, viewport);
		gluOrtho2D(-VIEW_WIDTH / 2, VIEW_WIDTH / 2, -VIEW_HEIGHT / 2, VIEW_HEIGHT / 2);  //拾取矩阵乘以投影矩阵,这样就可以让选择框放大为和视体一样大  
		drawGraph(GL_SELECT);//该函数中渲染物体,并且给物体设定名字
		glPopMatrix();//退出选择模式
		glMatrixMode(GL_MODELVIEW);//这里有一个修改??
		hits = glRenderMode(GL_RENDER);//从选择模式返回正常模式,该函数返回选择到对象的个数
		//glFlush();
		processHits(hits, selectBuf);//选择结果处理
		//glPopName();
		glutPostRedisplay();
	}
}

void myMouseMove(int x, int y){
	if (left_down){
		GLfloat d_x = 2.2*(x - o_x) * VIEW_WIDTH / WIN_WIDTH / 4;
		GLfloat d_y = 2.2*(o_y - y) * VIEW_HEIGHT / WIN_HEIGHT / 4;

		//将鼠标偏移量旋转-theta  
		//t_x += d_x*cos(-2 * PI*theta / 360) - d_y*sin(-2 * PI*theta / 360);
		//t_y += d_x*sin(-2 * PI*theta / 360) + d_y*cos(-2 * PI*theta / 360);

		//记录下鼠标的坐标  
		o_x = x;
		o_y = y;	
		//鼻子
		if (select_part == NOSE){
			nose[0] += d_x;
			nose[1] += d_y;
			drawNose();
		}	
		//左眼
		if (select_part == EYES){
			eye[0] += d_x;
			eye[1] += d_y;
			drawEye();
		}
		if (select_part == SKIMMER){
			skimmer[0] += d_x;
			skimmer[1] += d_y;
			drawSkimmer();
		}
	}	
	glutPostRedisplay();
}
void myKeyboard(unsigned char key, int x, int y) {
	switch (key)
	{
	}
}

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;
	}
}

int main(int argc, char** argv){
	glutInit( &argc, argv);//初始化opengl
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);

	glutInitWindowSize(500,500);
	glutInitWindowPosition(0, 0);
	glutCreateWindow("OpenGL-exp1");
	
	glutReshapeFunc(myReshape);//glutReshapeFunc是窗口改变的时候调用的函数,在这个里面可以根据缩放后的窗口重新设置camera的内部参数,比如横纵比啥的,而glutDisplayFunc是显示的时候调用,可见改变窗口后调用glutReshapeFunc后还是会调用glutDisplayFunc进行显示的。
	glutDisplayFunc(myDisplay);
	glutMouseFunc(myMouse);
	glutMotionFunc(myMouseMove);
	glutKeyboardFunc(myKeyboard);
	glutSpecialFunc(mySpecial);

	glutCreateMenu(processMenuEvents);
	glutAddMenuEntry("选项", -1);
	glutAddMenuEntry("黑色", 0);
	glutAddMenuEntry("红色", 1);
	glutAddMenuEntry("蓝色", 3);
	glutAddMenuEntry("紫色", 5);
	glutAddMenuEntry("黄色", 6);
	glutAddMenuEntry("白色", 7);
	glutAttachMenu(GLUT_RIGHT_BUTTON);

	myInit();//指定绘制的永久环境
	glutMainLoop();
}


其实为了完成实验,中间有一些投机取巧的地方,比如附加功能二,哎,罪过罪过。。。。

不过觉得自己画的这个是蛮好看的,♪(^∇^*)

实验结果:


  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值