2023年5月17日 星期三

小葉老師一步步教圖學 Week14 計時器timer、內插

Week14

電腦圖學 2023-05-17 Week14

1. 主題: 切換關節

2. 主題: 切換移動、旋轉

3. 整合打光、貼圖

4. 主題: timer計時器、內插動作



1. 請先裝 Git, 把我們的專案clone下載

- git clone 你的網址(比較慢)

2. 今天的程式: timer

- 先把 freeglut 再裝一次, 先照開學前幾週的作法

- glutTimerFunc(等多久, timer, 參數);

- 另外要準備好 void timer(int t) { ... }

- CodeBlocks: File-New-Project, GLUT專案 week14-1_timer 專案


step01-1

## step01-1_今天第一個主題, 是學會 timer()計時器的技巧。要用到 glutTimerFunc(等多久, timer, 參數)來設定。等的時間單位是 ms 千分之一秒。在範例裡,執行時會先等2秒鐘,然後開始第一個timer叫醒。timer()會改變angle值, 並更新畫面。巧妙的地方, 在 timer()裡, 又會再設定下一個timer()啟動的時間

```cpp

///Week14-1_timer

#include <GL/glut.h>

float angle=0;

void display()

{

    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

    glPushMatrix();

        glRotatef(angle, 0, 0, 1);

        glutSolidTeapot( 0.3 );

    glPopMatrix();

    glutSwapBuffers();

}

void timer(int t) ///step01-1 你的 timer()函式,做對應動作

{

    glutTimerFunc(500, timer, t+1); ///step01-1 設定下一個鬧鐘

    angle += 90; ///增加90度

    glutPostRedisplay(); ///重畫畫面

}

int main(int argc, char** argv)

{

    glutInit(&argc, argv);

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH);

    glutCreateWindow("week14");


    glutDisplayFunc(display);

    glutTimerFunc(2000, timer, 0); ///step01-1 設定timer函式


    glutMainLoop();

}

```













step01-2

## step01-2_新的專案 week14-2_timer_play 想要改用 keyboard()來觸發 timer() 而不要在 main()裡直接觸發。

```cpp

///Week14-2_timer_play 用鍵盤來播放

#include <GL/glut.h>

float angle=0;

void display()

{

    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

    glPushMatrix();

        glRotatef(angle, 0, 0, 1);

        glutSolidTeapot( 0.3 );

    glPopMatrix();

    glutSwapBuffers();

}

void timer(int t) ///step01-1 你的 timer()函式,做對應動作

{

    glutTimerFunc(500, timer, t+1); ///step01-1 設定下一個鬧鐘

    angle += 90; ///增加90度

    glutPostRedisplay(); ///重畫畫面

}

void keyboard(unsigned char key, int x, int y)

{

    glutTimerFunc(0, timer, 0);

}

int main(int argc, char** argv)

{

    glutInit(&argc, argv);

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH);

    glutCreateWindow("week14");


    glutDisplayFunc(display);

    glutKeyboardFunc(keyboard); ///step01-2 用 keyboard()

    ///glutTimerFunc(2000, timer, 0); ///step01-1 設定timer函式


    glutMainLoop();

}

```








step01-3

## step01-3_利用Excel來示範,什麼是alpha內插, 能用 alpha乘 新+(1-alpha)乘舊






step02-1

## step02-1_新的專案 week14-3_timer_alpha_interpolation, 裡面增加 mouse() 負責處理按下去、放開來,對應的動作。增加 motion() 負責即時更新角度,讓你看到角度。最後 timer()裡, 想要用 100格的內插, 所以t小於100時,持續設定下一次 timer, 等待的時間不要太久。alpha算出來, 用 alpha 來算出角度的值。


```cpp

///Week14-3_timer_alpha_interpolation

#include <GL/glut.h>

float angle=0, oldAngle=0, newAngle=0; ///step02-1 宣告變數

void display()

{

    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

    glPushMatrix();

        glRotatef(angle, 0, 0, 1);

        glutSolidTeapot( 0.3 );

    glPopMatrix();

    glutSwapBuffers();

}

void timer(int t) ///step01-1 你的 timer()函式,做對應動作

{

    if(t<100) glutTimerFunc(50, timer, t+1); ///step02-1 在100之內,設定下一個鬧鐘

    float alpha = t/ 100.0;///step02-1 alpha 介於0.00~1.00之間

    angle = newAngle*alpha + (1-alpha) * oldAngle; ///step02-1 alpha內插公式

    glutPostRedisplay(); ///重畫畫面

}

void motion(int x, int y) ///step02-1

{

    angle = x; ///step02-1 即時更新角度

    glutPostRedisplay(); ///step02-1 重畫畫面

}

void mouse(int button, int state, int x, int y) ///step02-1

{

    if(state==GLUT_DOWN) oldAngle = x; ///step02-1 按下去

    if(state==GLUT_UP) newAngle = x; ///step02-1 放開來

    glutPostRedisplay(); ///step02-1 重畫畫面

}

void keyboard(unsigned char key, int x, int y)

{

    glutTimerFunc(0, timer, 0);

}

int main(int argc, char** argv)

{

    glutInit(&argc, argv);

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH);

    glutCreateWindow("week14");


    glutDisplayFunc(display);

    glutMouseFunc(mouse); ///step02-1 按下去,表示起點, 放開來,表示終點

    glutMotionFunc(motion); ///step02-1 當mouse在motion時,即時更新畫面

    glutKeyboardFunc(keyboard); ///step01-2 用 keyboard()


    glutMainLoop();

}

```











step02-2

## step02-2_回到Final_Project這個專案, 先利用Git指令, 把前面的 week14-1 到 week14-3 都備份到雲端。接下來, 開啟 Final_Project, 我們想要選定關節, 利用 keyboard()配合 ID變數,來做到。按'0'就把ID=0; 按'1'就把ID=1; 所以在 display()裡, if(ID==0) 就在畫頭的地方設定紅色, else設白色。這樣我們便能挑選不同的關節

```cpp

///week12-5_TRT_keyboard_mouse 要用 keyboard mouse 來操控

#include <stdio.h>

#include <GL/glut.h>

#include "glm.h" ///week13 step02-1

GLMmodel * head = NULL; ///week13 step02-1

GLMmodel * body = NULL; ///week13 step02-1

GLMmodel * uparmR = NULL; ///week13 step02-1

GLMmodel * lowarmR = NULL; ///week13 step02-1

int show[4] = {1, 1, 1, 1};/// week14_step02-2 用 show[i] 來決定要不要顯示

int ID = 0;///0:頭 1身體 2上手臂 3下手臂  ///week14_step02-2

void keyboard(unsigned char key, int x, int y) {/// week13 step03-1

    if(key=='0') ID = 0; ///week14_step02-2

    if(key=='1') ID = 1; ///week14_step02-2

    if(key=='2') ID = 2; ///week14_step02-2

    if(key=='3') ID = 3; ///week14_step02-2

    ///if(key=='0') show[0] = !show[0];/// week13 step03-1

    ///if(key=='1') show[1] = !show[1];/// week13 step03-1

    ///if(key=='2') show[2] = !show[2];/// week13 step03-1

    ///if(key=='3') show[3] = !show[3];/// week13 step03-1

    glutPostRedisplay();

} ///原來的keyboard先註解、不要用

FILE * fout = NULL; ///step02-2 一開始,檔案沒有開, NULL

FILE * fin = NULL; ///step02-2 要讀檔用的指標, 一開始也是 NULL

float teapotX=0, teapotY=0; ///幫我們看移動值

float angle=0, angle2=0, angle3=0;///step03-2 擺動作

void display()

{

    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

    glPushMatrix();

        glScalef(0.2, 0.2, 0.2); ///week13_step02-2

        if(body==NULL){///week13 step02-1

            head = glmReadOBJ("model/head.obj");

            body = glmReadOBJ("model/body.obj");///week13 step02-1

            uparmR = glmReadOBJ("model/uparmR.obj");///Week13 step03-1

            lowarmR = glmReadOBJ("model/lowarmR.obj");///Week13 step03-1

            ///glmUnitize(body); ///week13 step02-1 這行之後會改

        }

        if(ID==0) glColor3f(1,0,0); ///選定的,設紅色

        else glColor3f(1,1,1); ///沒選定,設白色

        if(show[0]) glmDraw(head, GLM_MATERIAL);///Week13 step03-1


        if(ID==1) glColor3f(1,0,0); ///選定的,設紅色

        else glColor3f(1,1,1); ///沒選定,設白色

        if(show[1]) glmDraw(body, GLM_MATERIAL);///week13 step02-1這行之後會改


        glPushMatrix();

        

            glTranslatef(teapotX, teapotY, 0);


            if(ID==2) glColor3f(1,0,0); ///選定的,設紅色

            else glColor3f(1,1,1); ///沒選定,設白色

            if(show[2]) glmDraw(uparmR, GLM_MATERIAL);///Week13 step03-1

        glPopMatrix();


        if(ID==3) glColor3f(1,0,0); ///選定的,設紅色

        else glColor3f(1,1,1); ///沒選定,設白色

        if(show[3]) glmDraw(lowarmR, GLM_MATERIAL);///Week13 step03-1

    glPopMatrix();

    glutSwapBuffers();

}

int oldX = 0, oldY = 0; ///Week13 step03-2

void motion(int x, int y){ ///Week13 step03-2

    teapotX += (x - oldX)/150.0; ///Week13 step03-2

    teapotY -= (y - oldY)/150.0; ///Week13 step03-2

    oldX = x;

    oldY = y;

    printf("glTranslatef(%f, %f, 0);\n", teapotX, teapotY);

    glutPostRedisplay(); ///Week13 step03-2

} ///Week13 step03-2

void mouse(int button, int state, int x, int y)

{

    if(state==GLUT_DOWN){

        oldX = x; ///teapotX = (x-150)/150.0;

        oldY = y; ///teapotY = (150-y)/150.0;

        angle = x;

        ///printf("glTranslatef(%f, %f, 0);\n", teapotX, teapotY);

        ///if(fout==NULL) fout = fopen("file4.txt", "w"); ///step02-2 沒開檔,就開

        ///fprintf(fout, "%f %f\n", teapotX, teapotY); ///step02-2 要再存座標

    }

    display();

}

//void keyboard(unsigned char key, int x, int y) ///step02-2 keyboard函式

//{

//    if(fin==NULL){ ///step02-2 如果檔案還沒 fopen(), 就開它

//        fclose(fout); ///前面mouse會開fout指標, 所以要關掉

//        fin = fopen("file4.txt", "r"); ///step02-2 沒開檔,就開

//    }

//    ///fscanf(fin, "%f %f", &teapotX, &teapotY); ///step02-2 真的讀檔

//    display(); ///step02-2 重畫畫面

//}

int main(int argc, char** argv)

{

    glutInit(&argc, argv);

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH);

    glutCreateWindow("week12");


    glutDisplayFunc(display);

    glutMouseFunc(mouse);

    glutMotionFunc(motion); ///Week13 step03-3

    glutKeyboardFunc(keyboard); ///step02-2 keyboard要做事囉(開檔、讀檔)


    glutMainLoop();

}

```

















step03-1

## step03-1_利用 teapotX, teapotY 來協助找到 T-R-T 的關鍵 glTranslatef()的值, 讓旋轉中心能正確定位。這樣能做一正確旋轉的上手臂。再利用 glPushMatrix() glPopMatrix() 加深一層後, 把下手臂也做出來。


```cpp

///week12-5_TRT_keyboard_mouse 要用 keyboard mouse 來操控

#include <stdio.h>

#include <GL/glut.h>

#include "glm.h" ///week13 step02-1

GLMmodel * head = NULL; ///week13 step02-1

GLMmodel * body = NULL; ///week13 step02-1

GLMmodel * uparmR = NULL; ///week13 step02-1

GLMmodel * lowarmR = NULL; ///week13 step02-1

int show[4] = {1, 1, 1, 1};/// week14_step03-1 用 show[i] 來決定要不要顯示

int ID = 3;///0:頭 1身體 2上手臂 3下手臂  ///week14_step03-1

void keyboard(unsigned char key, int x, int y) {/// week13 step03-1

    if(key=='0') ID = 0; ///week14_step02-2

    if(key=='1') ID = 1; ///week14_step02-2

    if(key=='2') ID = 2; ///week14_step02-2

    if(key=='3') ID = 3; ///week14_step02-2

    ///if(key=='0') show[0] = !show[0];/// week13 step03-1

    ///if(key=='1') show[1] = !show[1];/// week13 step03-1

    ///if(key=='2') show[2] = !show[2];/// week13 step03-1

    ///if(key=='3') show[3] = !show[3];/// week13 step03-1

    glutPostRedisplay();

} ///原來的keyboard先註解、不要用

FILE * fout = NULL; ///step02-2 一開始,檔案沒有開, NULL

FILE * fin = NULL; ///step02-2 要讀檔用的指標, 一開始也是 NULL

float teapotX=0, teapotY=0; ///幫我們看移動值

float angle=0, angle2=0, angle3=0;///step03-2 擺動作

void display()

{

    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

    glPushMatrix();

        glScalef(0.2, 0.2, 0.2); ///week13_step02-2

        if(body==NULL){///week13 step02-1

            head = glmReadOBJ("model/head.obj");

            body = glmReadOBJ("model/body.obj");///week13 step02-1

            uparmR = glmReadOBJ("model/uparmR.obj");///Week13 step03-1

            lowarmR = glmReadOBJ("model/lowarmR.obj");///Week13 step03-1

            ///glmUnitize(body); ///week13 step02-1 這行之後會改

        }

        if(ID==0) glColor3f(1,0,0); ///選定的,設紅色

        else glColor3f(1,1,1); ///沒選定,設白色

        if(show[0]) glmDraw(head, GLM_MATERIAL);///Week13 step03-1


        if(ID==1) glColor3f(1,0,0); ///選定的,設紅色

        else glColor3f(1,1,1); ///沒選定,設白色

        if(show[1]) glmDraw(body, GLM_MATERIAL);///week13 step02-1這行之後會改


        glPushMatrix();

            glTranslatef(-1.200000, +0.453333, 0); ///wee14_step03-1 反過來

            glRotatef(angle, 0, 0, 1); ///week14_step03-1_TRT建出來

            //glTranslatef(teapotX, teapotY, 0); ///week14_step03-1_TRT建出來

            glTranslatef(1.200000, -0.453333, 0); ///week14_step03-1_的結果


            if(ID==2) glColor3f(1,0,0); ///選定的,設紅色

            else glColor3f(1,1,1); ///沒選定,設白色

            if(show[2]) glmDraw(uparmR, GLM_MATERIAL);///Week13 step03-1

            glPushMatrix();

                glTranslatef(-1.959999, +0.113333, 0);

                glRotatef(angle, 0, 0, 1);

                glTranslatef(1.959999, -0.113333, 0);


                if(ID==3) glColor3f(1,0,0); ///選定的,設紅色

                else glColor3f(1,1,1); ///沒選定,設白色

                if(show[3]) glmDraw(lowarmR, GLM_MATERIAL);///Week13 step03-1

            glPopMatrix();

        glPopMatrix();


    glPopMatrix();

    glColor3f(0, 1, 0);///week14_step03-1 放個小茶壼,在正中心,當成參考點

    glutSolidTeapot( 0.02 );///week14_step03-1 放個小茶壼,在正中心,當成參考點

    glutSwapBuffers();

}

int oldX = 0, oldY = 0; ///Week13 step03-2

void motion(int x, int y){ ///Week13 step03-2

    teapotX += (x - oldX)/150.0; ///Week13 step03-2

    teapotY -= (y - oldY)/150.0; ///Week13 step03-2

    oldX = x;

    oldY = y;

    angle = x; ///week14_step03-1

    printf("glTranslatef(%f, %f, 0);\n", teapotX, teapotY);

    glutPostRedisplay(); ///Week13 step03-2

} ///Week13 step03-2

void mouse(int button, int state, int x, int y)

{

    if(state==GLUT_DOWN){

        oldX = x; ///teapotX = (x-150)/150.0;

        oldY = y; ///teapotY = (150-y)/150.0;

        angle = x;

        ///printf("glTranslatef(%f, %f, 0);\n", teapotX, teapotY);

        ///if(fout==NULL) fout = fopen("file4.txt", "w"); ///step02-2 沒開檔,就開

        ///fprintf(fout, "%f %f\n", teapotX, teapotY); ///step02-2 要再存座標

    }

    display();

}

//void keyboard(unsigned char key, int x, int y) ///step02-2 keyboard函式

//{

//    if(fin==NULL){ ///step02-2 如果檔案還沒 fopen(), 就開它

//        fclose(fout); ///前面mouse會開fout指標, 所以要關掉

//        fin = fopen("file4.txt", "r"); ///step02-2 沒開檔,就開

//    }

//    ///fscanf(fin, "%f %f", &teapotX, &teapotY); ///step02-2 真的讀檔

//    display(); ///step02-2 重畫畫面

//}

int main(int argc, char** argv)

{

    glutInit(&argc, argv);

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH);

    glutCreateWindow("week12");


    glutDisplayFunc(display);

    glutMouseFunc(mouse);

    glutMotionFunc(motion); ///Week13 step03-3

    glutKeyboardFunc(keyboard); ///step02-2 keyboard要做事囉(開檔、讀檔)


    glutMainLoop();

}

```














沒有留言:

張貼留言