计算机图形学OpenGL-矩形绘制和时钟绘制-付老师的实验一

1、在窗口中绘制一个矩形。分别实现键盘和鼠标对矩形的控制,例如:拖动,缩放。并添加菜单进行颜色修改,图元设置等属性操作。

#include <GL/glut.h>

// 矩形的位置和大小
GLfloat rectX = 100.0f;
GLfloat rectY = 100.0f;
GLfloat rectWidth = 200.0f;
GLfloat rectHeight = 100.0f;

// 鼠标拖动相关变量
bool isDragging = false;
int dragStartX = 0;
int dragStartY = 0;

// 键盘平移和缩放相关变量
GLfloat translateX = 0.0f;
GLfloat translateY = 0.0f;
GLfloat scale = 1.0f;

// 矩形颜色
GLfloat red = 0.0f;
GLfloat green = 0.0f;
GLfloat blue = 1.0f;

// 图元设置
GLint drawMode = GL_FILL; // 默认填充模式

// 绘制矩形
void drawRectangle() 
{
    glPolygonMode(GL_FRONT_AND_BACK, drawMode); // 设置图元绘制模式
    glPushMatrix();
    glTranslatef(rectX + translateX, rectY + translateY, 0.0f);
    glScalef(scale, scale, 1.0f);
    glBegin(GL_QUADS);
    glVertex2f(0.0f, 0.0f);//左下角
    glVertex2f(rectWidth, 0.0f);//右下角
    glVertex2f(rectWidth, rectHeight);//右上角
    glVertex2f(0.0f, rectHeight);//左上角
    glEnd();
   
    glPopMatrix();
}

// 处理键盘事件
void keyboardFunc(unsigned char key, int x, int y) {
    if (key == '+') {
        scale += 0.1f; // 放大矩形
    }
    else if (key == '-') {
        scale -= 0.1f; // 缩小矩形
    }
    else if (key == 'w') {
        translateY += 10.0f; // 上移矩形
    }
    else if (key == 's') {
        translateY -= 10.0f; // 下移矩形
    }
    else if (key == 'a') {
        translateX -= 10.0f; // 左移矩形
    }
    else if (key == 'd') {
        translateX += 10.0f; // 右移矩形
    }
    else if (key == 'f') {
        drawMode = GL_FILL; // 填充模式
    }
    else if (key == 'l') {
        drawMode = GL_LINE; // 线框模式
    }
    else if (key == 'p') {
        drawMode = GL_POINT; // 点模式
    }
    switch (key)
    {
    case 'R' : // 红色加深
        red += 0.1f; break;
    case 'r': // 红色变浅
        red -= 0.1f; break;
    case 'G':// 绿色加深
        green += 0.1f; break;
    case 'g':// 绿色变浅
       green -= 0.1f; break;
    case 'B':// 绿色变浅
        blue += 0.1f; break;
    case 'b':// 绿色变浅
        blue -= 0.1f; break;
    case 'q':// 绿色变浅
    { red = green = 0.0f; blue = 1.0f; }
        break;
    }
    
    glutPostRedisplay(); // 重新绘制
}

// 处理鼠标点击事件
void mouseFunc(int button, int state, int x, int y) {
    if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
        isDragging = true;
        dragStartX = x;
        dragStartY = y;
    }
    else if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
        isDragging = false;
    }
    else if (button == 3) { // 滚轮向前滚动
        scale += 0.1f; // 放大矩形
        glutPostRedisplay(); // 重新绘制
    }
    else if (button == 4) { // 滚轮向后滚动
        scale -= 0.1f; // 缩小矩形
        glutPostRedisplay(); // 重新绘制
    }
}

// 处理鼠标拖动事件
void motionFunc(int x, int y) {
    if (isDragging) {
        int deltaX = x - dragStartX;
        int deltaY = y - dragStartY;
        translateX += deltaX;
        translateY -= deltaY; // 注意坐标系差异,鼠标向上拖动实际上是y减小
     /*   鼠标向上拖动导致 y 坐标减小的原因是OpenGL中的窗口坐标系与屏幕坐标系的差异。

            在屏幕坐标系中,通常原点位于左上角,向下移动会使 y 坐标增大,而向上移动会使 y 坐标减小。

            而在OpenGL中,默认情况下使用的窗口坐标系,原点位于左下角,向上移动会使 y 坐标增大,而向下移动会使 y 坐标减小。

            当处理鼠标拖动事件时,鼠标的移动距离(`deltaX` 和 `deltaY`)是基于屏幕坐标系计算的。因此,为了将鼠标的移动与窗口坐标系保持一致,我们需要将 y 坐标的变化取反,即 `translateY -= deltaY`。

            这样做可以确保在OpenGL窗口中,当鼠标向上拖动时,矩形会相应地向上移动。*/
        dragStartX = x;
        dragStartY = y;
        glutPostRedisplay(); // 重新绘制
    }
}

// 处理菜单选择事件
void menuFunc(int value) {
    switch (value) {
    case 1: // 设置矩形颜色为红色加深
        red += 0.1f;
        break;
    case 2: // 设置矩形颜色为红色变浅
        red -= 0.1f;
        break;
    case 3: // 设置矩形颜色为绿色加深
        green += 0.1f;
        break;
    case 4: // 设置矩形颜色为绿色变浅
        green -= 0.1f;
        break;
    case 5: // 设置矩形颜色为蓝色加深
        blue += 0.1f;
        break;
    case 6: // 设置矩形颜色为蓝色变浅
        blue -= 0.1f;
        break;
    case 7: // 恢复初始颜色为蓝色
        red = 0.0f;
        green = 0.0f;
        blue = 1.0f;
        break;
    }
    glutPostRedisplay(); // 重新绘制
}

// 显示回调函数
void displayFunc() {
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f); // 设置清屏颜色为黑色
    glClear(GL_COLOR_BUFFER_BIT); // 清空颜色缓冲区

    glColor3f(red, green, blue); // 设置绘制颜色为红色
    drawRectangle(); // 绘制矩形

    glFlush(); // 刷新缓冲区,将绘制内容显示到屏幕上
}

int main(int argc, char** argv) {
    glutInit(&argc, argv); // 初始化GLUT
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); // 设置显示模式
    glutInitWindowSize(500, 500); // 设置窗口尺寸
    glutCreateWindow("OpenGL Rectangle"); // 创建窗口,标题为"OpenGL Rectangle"

    glMatrixMode(GL_PROJECTION); // 设置投影矩阵模式
    glLoadIdentity(); // 装载单位矩阵
    gluOrtho2D(0.0, 500.0, 0.0, 500.0); // 设置正交投影

    glutDisplayFunc(displayFunc); // 注册显示回调函数
    glutMouseFunc(mouseFunc); // 注册鼠标点击回调函数
    glutMotionFunc(motionFunc); // 注册鼠标拖动回调函数
    glutKeyboardFunc(keyboardFunc); // 注册键盘回调函数

    
    GLint menuColor = glutCreateMenu(menuFunc);
    glutAddMenuEntry("Red++", 1);
    glutAddMenuEntry("Red--", 2);
    glutAddMenuEntry("Green++", 3);
    glutAddMenuEntry("Green--", 4);
    glutAddMenuEntry("Blue++", 5);
    glutAddMenuEntry("Blue--", 6);

    GLint menuDrawMode = glutCreateMenu(menuFunc);
    glutAddMenuEntry("Fill", 7);
    glutAddMenuEntry("Wireframe", 8);
    glutAddMenuEntry("Points", 9);

    GLint mainMenu = glutCreateMenu(menuFunc);
    glutAddSubMenu("Color", menuColor);
    glutAddSubMenu("Draw Mode", menuDrawMode);
    glutAddMenuEntry("Reset", 10);

    glutAttachMenu(GLUT_RIGHT_BUTTON); // 将菜单绑定到右键
    glutMainLoop(); // 进入主循环,开始事件处理

    return 0;
}

初始状态是蓝色的矩形

鼠标点击拖动矩形,中键滚轮前后缩放矩形,

键盘按wasd键实现矩形向上、左、下、右平移,

按“+”键放大,按“-”键缩小,

按   q键恢复矩形颜色为蓝色,

R键是红色加深,r键是红色变浅

G键是绿色加深,g键是绿色变浅

B键是蓝色加深,b键是蓝色变浅

鼠标右键点击出现菜单,Draw Mode是图元操作,依次为填充模式,线框模式,点模式

2、编写一个时钟绘制程序。

glFlush()函数和glClear(GL_COLOR_BUFFER_BIT)函数的作用分别是什么?

(可将这两个函数注释掉,和注释前的结果对比)

(1)glFlush() 函数用于强制执行所有待处理的OpenGL命令,并将它们发送到渲染管线进行处理。它会等待所有先前的绘图命令执行完成,然后将绘图结果立即输出到屏幕上。

在双缓冲机制下,glFlush() 会将绘制命令刷新到后备缓冲区,但不会立即将后备缓冲区的内容显示在屏幕上。相反,它只是确保所有绘制命令都已提交到渲染管线,并等待渲染完成。

glFlush() 通常在需要强制立即显示绘制结果时使用,例如在交互式绘图或实时渲染应用中。

注释掉后:

(2)glClear(GL_COLOR_BUFFER_BIT) 函数用于清除颜色缓冲区的内容。它将指定的颜色值应用于整个颜色缓冲区,使其完全填充为指定的颜色。

颜色缓冲区是用于存储绘制图形的像素颜色值的内存区域。调用glClear(GL_COLOR_BUFFER_BIT) 会清除颜色缓冲区的内容,将其重置为初始状态。通常,这个函数会在每次绘制新帧之前调用,以确保在绘制新图形之前清除之前的内容。GL_COLOR_BUFFER_BIT 是一个标志位,用于指示需要清除颜色缓冲区。

注释掉后:

(1)尝试直线与圆的绘制。

(2)尝试旋转与移动等功能。

#include <GL/glut.h>
#include <math.h>

#define PI 3.14159265358979323846

float angle_hour = 90.0;
float angle_minute = 90.0;
float angle_second = 90.0;

void init()
{
    glClearColor(1.0f, 1.0f, 1.0f, 0.0);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(-1.0, 1.0, -1.0, 1.0);
}


void drawClock()
{
	glClear(GL_COLOR_BUFFER_BIT);

	// 设置圆环
	glColor3f(1.0f, 0.0f, 1.0f);
	glBegin(GL_LINE_LOOP);
	for (int i = 0; i < 360; i++)//从0开始迭代到359,总共循环360次。在圆形边界上绘制足够多的点,使得最终看起来像一个圆。
	{
		float angle = float(2 * PI * i / 360);//计算当前循环迭代的角度。我们将角度范围从0到360度映射到0到2π(弧度制)范围。PI是圆周率,即3.14159265358979323846。
		float x = float(0.8 * cos(angle));//计算当前角度下的x坐标。通过使用三角函数cos(),将角度转换为x轴上的坐标值。这里将x轴的长度缩放为0.8,使得圆的半径为0.8。
		float y = float(0.8 * sin(angle));//计算当前角度下的y坐标。通过使用三角函数sin(),将角度转换为y轴上的坐标值。同样,y轴的长度也缩放为0.8。
		glVertex2f(x, y);
	}
	glEnd();

	//画时针
	glPushMatrix();
    glRotatef(angle_hour, 0.0, 0.0, 1.0);
    glColor3f(1.0, 1.0, 0.0);  //黄色
    glBegin(GL_LINES);
    glVertex2f(0.0, 0.0);
    glVertex2f(0.2, 0.0);
    glEnd();
    glPopMatrix();

    // 画分针
    glPushMatrix();
    glRotatef(angle_minute, 0.0, 0.0, 1.0);
    glColor3f(0.0, 1.0, 1.0);  //青色
    glBegin(GL_LINES);
    glVertex2f(0.0, 0.0);
    glVertex2f(0.4, 0.0);
    glEnd();
    glPopMatrix();

    // 画秒针
    glPushMatrix();
    glRotatef(angle_second, 0.0, 0.0, 1.0);
    glColor3f(1.0, 0.0, 1.0);//紫色
    glBegin(GL_LINES);
    glVertex2f(0.0, 0.0);
    glVertex2f(0.6, 0.0);
    glEnd();
    glPopMatrix();

    glFlush();
}

void update(int value)
{
    angle_second -= 6.0;  
    /*每次调用时,秒针的旋转角度减去6度。这里的6.0是指秒针每秒钟旋转的角度,通过将整个圆的360度除以60秒,得到每秒钟旋转的角度为6度。*/
    angle_minute -= 0.1;  
    //每次调用时,分针的旋转角度减去0.1度。这里的0.1是指分针每秒钟旋转的角度,通过将整个圆的360度除以60分钟,得到每秒钟旋转的角度为0.1度。
    angle_hour -= 1.0 / 120;  
    // 每次调用时,时针的旋转角度减去1/120度。这里的1.0 / 120是指时针每秒钟旋转的角度,360/12/60/60=1/120通过将整个圆的360度除以7200秒,得到每秒钟旋转的角度为1/120度。

    glutPostRedisplay();
    glutTimerFunc(1000, update, 0);//注册一个定时器,每隔1000毫秒(即1秒)调用一次 update() 函数,以便更新时钟指针的旋转角度。
    //在update函数的末尾,要再次调用 glutTimerFunc 来注册下一次定时器,以便创建循环的定时器效果。
}

void keyboard(unsigned char key, int x, int y)
{
    switch (key)
    {
    case 'a':
        angle_hour += 30.0;  // 时针逆时针转一个数字转30度
        break;
    case 's':
        angle_hour -= 30.0;   // 时针顺时针转一个数字转30度
        break;
    case 'd':
        angle_minute += 6.0;  // 分针逆时针转一个数字转6度
        break;
    case 'f':
        angle_minute -= 6.0; // 分针顺时针转一个数字转6度
        break;
    case 'g':
        angle_second += 6.0;  // 秒针逆时针转一个数字转6度
        break;
    case 'h':
        angle_second -= 6.0;  //秒针顺时针转一个数字转6度
        break;
    }

    glutPostRedisplay();
}

int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(500, 500);
    glutInitWindowPosition(100, 100);
    glutCreateWindow("Clock");
    init();
    glutDisplayFunc(drawClock);
    glutTimerFunc(1000, update, 0);
    glutKeyboardFunc(keyboard);
    glutMainLoop();

    return 0;
}

以上程序绘制一个简单的时钟,时针、分针和秒针分别通过键盘控制顺时针和逆时针旋转。按下 'a' 键时,时针顺时针旋转30度;按下 's' 键时,时针逆时针旋转30度;按下 'd' 键时,分针顺时针旋转6度;按下 'f' 键时,分针逆时针旋转6度;按下 'g' 键时,秒针顺时针旋转6度;按下 'h' 键时,秒针逆时针旋转6度。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值