📜  在 OpenGL 中使用鼠标移动绘制圆的程序(1)

📅  最后修改于: 2023-12-03 15:23:14.436000             🧑  作者: Mango

在 OpenGL 中使用鼠标移动绘制圆的程序

本程序可以通过鼠标移动来绘制一个圆。

使用的技术
  • OpenGL(OpenGL Utility Toolkit)
  • GLUT(OpenGL Utility Toolkit 外部函数库)
程序流程
  1. 初始化 OpenGL 环境(包括窗口等);
  2. 设置回调函数(display、reshape 和 mouse 函数);
  3. 在 display 函数中,根据鼠标移动计算圆心坐标和半径,并绘制圆;
  4. 在 reshape 函数中,设置视角;
  5. 在 mouse 函数中,处理鼠标移动事件。
详细介绍
1. 初始化 OpenGL 环境

在程序开始时,需要初始化 OpenGL 环境,包括创建窗口、设置视角、添加光源等。

glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(500, 500);
glutInitWindowPosition(100, 100);
glutCreateWindow("Draw Circle with Mouse");
glClearColor(0.0, 0.0, 0.0, 0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-1.0, 1.0, -1.0, 1.0);
2. 设置回调函数

回调函数用于处理不同事件,比如绘制图形、改变窗口大小、鼠标事件等。

glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMouseFunc(mouse);
3. 绘制圆

圆的绘制可以利用 OpenGL 的内置函数 gluDisk(),但是这个函数是按半径绘制的,不利于鼠标移动时动态地绘制圆。

我们可以根据鼠标移动的距离来计算圆心坐标和半径,然后利用 OpenGL 的 GL_LINE_LOOP 模式来绘制圆:

float center_x = 0, center_y = 0, radius = 0;
int is_drawing_circle = 0;

void display()
{
    glClear(GL_COLOR_BUFFER_BIT);

    if (is_drawing_circle) {
        glColor3f(1.0, 1.0, 1.0);
        glBegin(GL_LINE_LOOP);
        for (int i = 0; i < 360; i++) {
            float theta = 2.0f * M_PI * (i / 360.0f);
            float x = radius * cosf(theta);
            float y = radius * sinf(theta);
            glVertex2f(x + center_x, y + center_y);
        }
        glEnd();
    }

    glFlush();
}
4. 设置视角

在 reshape 函数中,我们需要设置视角。我们可以使用 glViewport()gluOrtho2D() 函数来设置视角:

void reshape(GLint w, GLint h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
        gluOrtho2D(-1.0, 1.0, -1.0 * (GLfloat) h / (GLfloat) w, 1.0 * (GLfloat) h / (GLfloat) w);
    else
        gluOrtho2D(-1.0 * (GLfloat) w / (GLfloat) h, 1.0 * (GLfloat) w / (GLfloat) h, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
}
5. 处理鼠标事件

我们需要处理鼠标事件,包括鼠标的按下、移动和释放事件。在本程序中,我们只需要处理鼠标的移动事件。

当鼠标移动时,我们可以使用 glutMotionFunc() 函数来处理事件。我们需要计算鼠标移动的距离,然后根据距离计算出圆心坐标和半径,最后利用 is_drawing_circle 变量来判断是否需要绘制圆。

void mouse(int button, int state, int x, int y)
{
    if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
        center_x = 2.0 * ((float) x / (float) glutGet(GLUT_WINDOW_WIDTH)) - 1.0;
        center_y = -2.0 * ((float) y / (float) glutGet(GLUT_WINDOW_HEIGHT)) + 1.0;
        radius = 0;
        is_drawing_circle = 1;
    }
    else if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
        is_drawing_circle = 0;
    }
}

void motion(int x, int y)
{
    if (is_drawing_circle) {
        float x_pos = 2.0 * ((float) x / (float) glutGet(GLUT_WINDOW_WIDTH)) - 1.0;
        float y_pos = -2.0 * ((float) y / (float) glutGet(GLUT_WINDOW_HEIGHT)) + 1.0;

        float dx = x_pos - center_x;
        float dy = y_pos - center_y;

        radius = sqrtf(dx * dx + dy * dy);

        glutPostRedisplay();
    }
}
完整代码
#include <GL/glut.h>
#include <cmath>

float center_x = 0, center_y = 0, radius = 0;
int is_drawing_circle = 0;

void display()
{
    glClear(GL_COLOR_BUFFER_BIT);

    if (is_drawing_circle) {
        glColor3f(1.0, 1.0, 1.0);
        glBegin(GL_LINE_LOOP);
        for (int i = 0; i < 360; i++) {
            float theta = 2.0f * M_PI * (i / 360.0f);
            float x = radius * cosf(theta);
            float y = radius * sinf(theta);
            glVertex2f(x + center_x, y + center_y);
        }
        glEnd();
    }

    glFlush();
}

void reshape(GLint w, GLint h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
        gluOrtho2D(-1.0, 1.0, -1.0 * (GLfloat) h / (GLfloat) w, 1.0 * (GLfloat) h / (GLfloat) w);
    else
        gluOrtho2D(-1.0 * (GLfloat) w / (GLfloat) h, 1.0 * (GLfloat) w / (GLfloat) h, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
}

void mouse(int button, int state, int x, int y)
{
    if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
        center_x = 2.0 * ((float) x / (float) glutGet(GLUT_WINDOW_WIDTH)) - 1.0;
        center_y = -2.0 * ((float) y / (float) glutGet(GLUT_WINDOW_HEIGHT)) + 1.0;
        radius = 0;
        is_drawing_circle = 1;
    }
    else if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
        is_drawing_circle = 0;
    }
}

void motion(int x, int y)
{
    if (is_drawing_circle) {
        float x_pos = 2.0 * ((float) x / (float) glutGet(GLUT_WINDOW_WIDTH)) - 1.0;
        float y_pos = -2.0 * ((float) y / (float) glutGet(GLUT_WINDOW_HEIGHT)) + 1.0;

        float dx = x_pos - center_x;
        float dy = y_pos - center_y;

        radius = sqrtf(dx * dx + dy * dy);

        glutPostRedisplay();
    }
}

int main(int argc, char **argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(500, 500);
    glutInitWindowPosition(100, 100);
    glutCreateWindow("Draw Circle with Mouse");
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMouseFunc(mouse);
    glutMotionFunc(motion);
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glutMainLoop();

    return 0;
}