C語言 計算機圖形學 OpenGL 繪製直線、三角形、矩形並實時預覽

老師給的題目並不難,我刻意複雜化了一下

  • 繪製圖像時,實時預覽將要生成的圖像,並非點擊了幾個點纔開始畫圖
  • 自己加了一個4象限對稱的畫圖模式(mirror)

當前存在的bug:

  1. 繼續畫圖之前,旋轉必須到360度(默認設置60度,即要旋轉6次後才能正常畫圖),不然像素點會和點擊的座標不一致
  2. 放大和縮小同上,有一次放大必須有一次縮小回來,反之亦然

圖形學實驗就開這幾周,暫時只能學到這裏了,歡迎留言!
附圖:(標題double buffered無視,代碼用的single,鼠標點擊右鍵觸發菜單)
在這裏插入圖片描述
假裝是五筒?

對稱畫圖模式(mirror)
在這裏插入圖片描述

#include <stdlib.h>
#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif


int ww = 500;
int wh = 500;

int need_rotate = 0;//旋轉標識
//color 便於修改顏色
float red = 1;
float green = 1;
float blue = 1;
//coordinates
struct cor
{
    float x;
    float y;
};//存儲座標結構體
//3個座標,用於存儲直線(2個)、三角形(3個)、矩形(2個)繪製時的座標
struct cor cc1;
struct cor cc2;
struct cor cc3;

int mode = 0;//選擇繪畫模式,line、triangle、rectangle三種,默認爲line
int dots_now = 0;//記錄已經確定的座標個數
struct pics
{
    int mode;
    struct cor c1;
    struct cor c2;
    struct cor c3;
};//一個圖形所有點的座標結構體
struct pics saved_cords[100];//可存儲100個圖形的座標和繪製模式
int ptr = -1;//如上結構體的計數指針
int reDrawMode =0;//重畫模式,用於繪製新圖形後,補畫之前的圖形
int mirrorMode =0;//鏡像模式
//func delc
void reDraw();
void draw_one_line(struct cor c1,struct cor c2);
void draw_triangle(struct cor c1,struct cor c2,struct cor c3);
void draw_rectangle(struct cor c1,struct cor c2);
void display();
void main_menu_func(int option);
void sub_menu_draw_mode_func(int option);
void sub_menu_move_func(int option);
void myinit(void);
void mouse_move(int x, int y);
void mouse(int btn, int state, int x, int y);
void myReshape(GLsizei w, GLsizei h);


void draw_one_line(struct cor c1,struct cor c2)
{//繪製直線函數
    if (need_rotate == 1)
    {//平移-旋轉-平移操作
        int mid_x = (c1.x + c2.x) / 2;
        int mid_y = (c1.y + c2.y) / 2;
        glTranslated(mid_x, mid_y, 0);
        glRotated(60, 0, 0, 1);
        glTranslated(-mid_x, -mid_y, 0);
        need_rotate = 0;
    }
    if(reDrawMode == 0)
    {//在重畫函數調用當前函數時,不需要清除背景
        glClear(GL_COLOR_BUFFER_BIT);
    }
    glColor3f(red, green, blue);
    if(mirrorMode == 0)
    {
        glBegin(GL_LINE_LOOP);
    }
    else
    {//mirror mode,鏡像畫圖,四個象限會對稱畫圖
        glBegin(GL_LINES);//lines,2個2個座標成對畫直線
        glVertex2f(c1.x,-c1.y);//4th
        glVertex2f(c2.x,-c2.y);
        glVertex2f(-c1.x,-c1.y);//3rd
        glVertex2f(-c2.x,-c2.y);
        glVertex2f(-c1.x,c1.y);//2nd
        glVertex2f(-c2.x,c2.y);
    }
    glVertex2f(c1.x, c1.y);
    glVertex2f(c2.x, c2.y);
    glEnd();
    glFlush();
}
void draw_triangle(struct cor c1,struct cor c2,struct cor c3)
{//繪製三角形
    if (need_rotate == 1)
    {//旋轉
        int mid_x = (c1.x + c2.x + c3.x) / 3;
        int mid_y = (c1.y + c2.y + c3.y) / 3;
        glTranslated(mid_x, mid_y, 0);
        glRotated(60, 0, 0, 1);
        glTranslated(-mid_x, -mid_y, 0);
        need_rotate = 0;
    }
    if(reDrawMode == 0)
    {
        glClear(GL_COLOR_BUFFER_BIT);
        //reDraw();
    }
    glColor3f(red, green, blue);
    if(mirrorMode == 0)
        glBegin(GL_TRIANGLES);
    else
    {
        glBegin(GL_TRIANGLES);//每3個座標就能繪製一個三角形
        glVertex2f(c1.x,-c1.y);//4th
        glVertex2f(c2.x,-c2.y);
        glVertex2f(c3.x,-c3.y);

        glVertex2f(-c1.x,-c1.y);//3rd
        glVertex2f(-c2.x,-c2.y);
        glVertex2f(-c3.x,-c3.y);

        glVertex2f(-c1.x,c1.y);//2nd
        glVertex2f(-c2.x,c2.y);
        glVertex2f(-c3.x,c3.y);
    }
        
    glVertex2f(c1.x, c1.y);
    glVertex2f(c2.x, c2.y);
    glVertex2f(c3.x, c3.y);
    glEnd();
    glFlush();

}
void draw_rectangle(struct cor c1,struct cor c2)
{//繪製矩形,用兩個點繪製,分別是左上和右下點
    if (need_rotate == 1)
    {//旋轉
        int mid_x = (c1.x + c2.x) / 2;
        int mid_y = (c1.y + c2.y) / 2;
        glTranslated(mid_x, mid_y, 0);
        glRotated(60, 0, 0, 1);
        glTranslated(-mid_x, -mid_y, 0);
        need_rotate = 0;
    }
    if(reDrawMode == 0)
    {
        glClear(GL_COLOR_BUFFER_BIT);
        //reDraw();
    }
    glColor3f(red, green, blue);
    if(mirrorMode == 0)
        glBegin(GL_QUADS);
    else
    {
        glBegin(GL_QUADS);//每4個座標就能繪製出一個矩形
        glVertex2f(c1.x,-c1.y);//4th
        glVertex2f(c1.x,-c2.y);
        glVertex2f(c2.x,-c2.y);
        glVertex2f(c2.x,-c1.y);

        glVertex2f(-c1.x,-c1.y);//3rd
        glVertex2f(-c1.x,-c2.y);
        glVertex2f(-c2.x,-c2.y);
        glVertex2f(-c2.x,-c1.y);

        glVertex2f(-c1.x,c1.y);//2nd
        glVertex2f(-c1.x, c2.y);
        glVertex2f(-c2.x, c2.y);
        glVertex2f(-c2.x, c1.y);
    }
        // glBegin(GL_LINE_LOOP);
    glVertex2f(c1.x, c1.y);
    glVertex2f(c1.x, c2.y);
    glVertex2f(c2.x, c2.y);
    glVertex2f(c2.x, c1.y);
    glEnd();
    glFlush();

}
void display()
{//顯示函數
    if (mode == 0)
    { //draw line
        draw_one_line(cc1,cc2);
    }
    if (mode == 1)
    { //draw triangle
        draw_triangle(cc1,cc2,cc3);
    }
    if (mode == 2)
    { //draw rectangle
        draw_rectangle(cc1,cc2);
    }
    //dots_now = 0;
}
void main_menu_func(int option)
{
    if (option == 0)
    { //Set pic to random color
        srand(time(NULL));
        red = rand() % 100 / 100.00;
        green = rand() % 100 / 100.00;
        blue = rand() % 100 / 100.00;
    }
    if (option == 1)
    { //zoom in 放大(縮放投影範圍)
        ww -= 50;
        wh -= 50;
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(-ww, ww, -wh, wh, -1.0, 1.0);
    }
    if (option == 2)
    { //zoom out
        ww += 50;
        wh += 50;
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(-ww, ww, -wh, wh, -1.0, 1.0);
    }
    if (option == 3)
    { //rotate
        need_rotate = 1;
    }
    if (option == 4)
    { //clear 清除屏幕上的圖形
        glClear(GL_COLOR_BUFFER_BIT);
        ptr = -1;
        glFlush();
        return;
    }
    if (option == 5)
    { //mirror mode
        mirrorMode += 1;
        mirrorMode %= 2;//兩種取值範圍,0(關),1(開),重複點擊切換
    }
    if (option == -1)
    { //exit test
        exit(0);
    }
    if (mode == 0)
        draw_one_line(cc1,cc2);
    if (mode == 1)
        draw_triangle(cc1,cc2,cc3);
    if (mode == 2)
        draw_rectangle(cc1,cc2);
}
void sub_menu_draw_mode_func(int option)
{//繪製模式子菜單
    if (option == 0)
    {
        mode = 0; //line mode
        dots_now = 0;
    }

    if (option == 1)
    {
        mode = 1; //tri mode
        dots_now = 0;
    }

    if (option == 2)
    {
        mode = 2; //rect mode
        dots_now = 0;
    }
}
void sub_menu_move_func(int option)
{//移動圖形子菜單
    if (option == 0)
    { //up
        cc1.y += 50;
        cc2.y += 50;
        cc3.y += 50;
    }
    if (option == 1)
    { //down
        cc1.y -= 50;
        cc2.y -= 50;
        cc3.y -= 50;
    }
    if (option == 2)
    { //left
        cc1.x -= 50;
        cc2.x -= 50;
        cc3.x -= 50;
    }
    if (option == 3)
    { //right
        cc1.x += 50;
        cc2.x += 50;
        cc3.x += 50;
    }
    if (mode == 0)
        draw_one_line(cc1,cc2);
    if (mode == 1)
        draw_triangle(cc1,cc2,cc3);
    if (mode == 2)
        draw_rectangle(cc1,cc2);
}
void myinit(void)
{
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glShadeModel(GL_FLAT);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-ww, ww, -wh, wh, -1.0, 1.0);
}
void mouse_move(int x, int y)
{//監聽鼠標移動函數,用於實時畫圖
    if (dots_now == 1 && (mode == 0 || mode == 1 || mode == 2))
    { //這種情況下只能預覽出線條或矩形
        printf("mouse loc x:%d,y%d\n", x, y);
        //  sleep(1);
        y = wh - y;//以下爲座標系變換
        x -= ww / 2;
        y -= wh / 2;
        x *= 2 * ww / 500;
        y *= 2 * wh / 500;
        cc2.x = x;
        cc2.y = y;
        if (mode != 2)
            draw_one_line(cc1,cc2);
        else
            draw_rectangle(cc1,cc2);
    }
    if (dots_now == 2 && mode == 1)
    { //tri 已經畫出了一條線,預覽即將繪製的三角形的情況
        printf("mouse loc x:%d,y%d\n", x, y);
        y = wh - y;//以下爲座標系變換
        x -= ww / 2;
        y -= wh / 2;
        x *= 2 * ww / 500;
        y *= 2 * wh / 500;
        cc3.x = x;
        cc3.y = y;
        draw_triangle(cc1,cc2,cc3);
    }
}
void mouse(int btn, int state, int x, int y)
{//每次鼠標左鍵點擊記錄畫圖座標

    if (btn == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
    {
        printf("click:x:%d,y:%d\n", x, y);

        printf("\tc1.x=%f,c1.y=%f\n", cc1.x, cc1.y);
        printf("\tc2.x=%f,c2.y=%f\n", cc2.x, cc2.y);
        printf("\tc3.x=%f,c3.y=%f\n", cc3.x, cc3.y);
        //座標系變換
        y = wh - y;
        x -= ww / 2;
        y -= wh / 2;
        x *= 2 * ww / 500;
        y *= 2 * wh / 500;

        switch (dots_now)
        {
        case 0:
            cc1.x = x;
            cc1.y = y;
            break;
        case 1:
            cc2.x = x;
            cc2.y = y;
            if (mode != 1)
            {//不等於三角形模式,說明已經記錄了兩個點,馬上確定繪製
                dots_now = -1;//重置點的計數器
                    ++ptr;
                if(mode == 0)
                    saved_cords[ptr].mode = 0;//記錄當前圖形的繪製模式,用於重畫
                else if(mode == 2)
                    saved_cords[ptr].mode = 2;//記錄當前圖形的繪製模式,用於重畫
                saved_cords[ptr].c1 = cc1;
                saved_cords[ptr].c2 = cc2;
                //reDraw();
            }
            break;
        case 2:
            cc3.x = x;
            cc3.y = y;

                ++ptr;
            saved_cords[ptr].mode = 1;//記錄當前圖形的繪製模式,用於重畫
            saved_cords[ptr].c1 = cc1;
            saved_cords[ptr].c2 = cc2;
            saved_cords[ptr].c3 = cc3;
            dots_now = -1;//重置點的計數器
            //reDraw();
            break;
        default:
            break;
        }

        ++dots_now;
    }
}
void reDraw()
{//每畫一個新的圖形後,把以前繪製的圖形全部重畫一次
    reDrawMode = 1;
    for(int i=0;i<=ptr;++i)
    {
        if(saved_cords[i].mode == 0)
        {
            draw_one_line(saved_cords[i].c1,saved_cords[i].c2);
            continue;
        }
        if(saved_cords[i].mode == 1)
        {
            draw_triangle(saved_cords[i].c1,saved_cords[i].c2,saved_cords[i].c3);
            continue;
        }
        if(saved_cords[i].mode == 2)
        {
            draw_rectangle(saved_cords[i].c1,saved_cords[i].c2);
            continue;
        }
        
    }
    reDrawMode = 0;
}
void myReshape(GLsizei w, GLsizei h)
{
    /* adjust clipping box */

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-w, w, -h, h, -1.0, 1.0);

    /* adjust viewport and clear */

    glViewport(0, 0, w, h);
    ww = w;
    wh = h;
}

/*  Main Loop
 *  Open window with initial window size, title bar, 
 *  RGBA display mode, and handle input events.
 */
int main(int argc, char **argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(ww, wh);
    glutCreateWindow("my window");

    int sub_menu1 = glutCreateMenu(sub_menu_draw_mode_func);
        glutAddMenuEntry("line", 0);
        glutAddMenuEntry("triangle", 1);
        glutAddMenuEntry("rectangle", 2);
    int sub_menu2 = glutCreateMenu(sub_menu_move_func);
        glutAddMenuEntry("Up", 0);
        glutAddMenuEntry("Down", 1);
        glutAddMenuEntry("Left", 2);
        glutAddMenuEntry("Right", 3);
    glutCreateMenu(main_menu_func);
        glutAddSubMenu("Draw:", sub_menu1);
        glutAddSubMenu("Move:", sub_menu2);
        glutAddMenuEntry("Set new color", 0);
        glutAddMenuEntry("Zoom In", 1);
        glutAddMenuEntry("Zoom Out", 2);
        glutAddMenuEntry("Rotate", 3);
        glutAddMenuEntry("Mirror",5);
        glutAddMenuEntry("Clear",4);
        glutAddMenuEntry("Exit", -1);

    glutAttachMenu(GLUT_RIGHT_BUTTON);
    glutDisplayFunc(display);
    glutReshapeFunc(myReshape);
    glutPassiveMotionFunc(mouse_move);
    glutIdleFunc(reDraw);
    glutMouseFunc(mouse);
    myinit();
    glutMainLoop();
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章