您的位置:首页 > 产品设计 > UI/UE

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

2017-05-21 23:17 501 查看
使用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();

e812
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]);

//移动到指定位置,改变状态为wearing
if (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();
}


程序运行结果:



选中部件可以进行拖动:



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



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



右键可以将旗子换色:



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息