程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> opengl基礎學習專題 (二) 點直線和多邊形,opengl專題

opengl基礎學習專題 (二) 點直線和多邊形,opengl專題

編輯:關於C語言

opengl基礎學習專題 (二) 點直線和多邊形,opengl專題


題外話

  隨著學習的增長,越來越覺得自己很水.關於上一篇博文中推薦用一個 學習opengl的 基於VS2015的 simplec框架.存在

一些問題.

   1.這個框架基於VS 的Debug 模式下,沒有考慮Release版本

   2.這個版本中chead,c基礎頭文件中有些宏設計的不好,例如

//4.0 控制台打印錯誤信息
#ifndef CERR
#define CERR(fmt,...) \
	fprintf(stderr,fmt,##__VA_ARGS__),putchar('\n')
#endif/* !CERR */

//4.1 控制台打印錯誤信息並退出
#ifndef ERR_EXIT
#define ERR_EXIT(fmt,...) \
	CERR(fmt,##__VA_ARGS__),exit(EXIT_FAILURE)
#endif/* !ERR */

    更加合理的是

//錯誤打印宏
#define CERR_EXIT(fmt,...) \
 fprintf(stderr,"[%s:%d]", __FILE__, __LINE__), fprintf(stderr, fmt, ##__VA_ARGS__), fputs("\r\n",stderr)

    這裡的輔助宏需要重新設計

    3.原先的sc_log 模塊,簡單的日志記錄系統.適用於單用戶多線程模式.隨著項目的增加,需要支持多用戶.需要重構,讓其具備海量多用戶日志記錄能力

      4.需要添加一個Session模塊,適用多用戶

    5.內部有些模塊需要重構,用樹結構替代鏈表結構,提高查找速率.

以上這些就是推薦學習opengl 使用 simplec框架目前不爽的地方.爭取下一個版本全部搞好.加班太多了,真不想......

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

分享一個程序員傷感的故事

  有一天晚上,兩個程序員吃完飯,碰見了不該碰見的人.發現自己都是備胎,

互相安慰說.

    還是上去寫寫代碼壓壓驚吧.哈哈

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

正題

本次課程所要講的是繪制簡單的幾何圖形,在實際繪制之前,讓我們先熟悉一些概念。

一、點、直線和多邊形
我們知道數學(具體的說,是幾何學)中有點、直線和多邊形的概念,但這些概念在計算機中會有所不同。
數學上的點,只有位置,沒有大小。但在計算機中,無論計算精度如何提高,始終不能表示一個無窮小的點。另一方面,無論圖形輸出設備(例如,顯示器)如何精確,始終不能輸出一個無窮小的點。一般情況下,OpenGL中的點將被畫成單個的像素(像素的概念,請自己搜索之~),雖然它可能足夠小,但並不會是無窮小。同一像素上,OpenGL可以繪制許多坐標只有稍微不同的點,但該像素的具體顏色將取決於OpenGL的實現。當然,過度的注意細節就是鑽牛角尖,我們大可不必花費過多的精力去研究“多個點如何畫到同一像素上”。
同樣的,數學上的直線沒有寬度,但OpenGL的直線則是有寬度的。同時,OpenGL的直線必須是有限長度,而不是像數學概念那樣是無限的。可以認為,OpenGL的“直線”概念與數學上的“線段”接近,它可以由兩個端點來確定。
多邊形是由多條線段首尾相連而形成的閉合區域。OpenGL規定,一個多邊形必須是一個“凸多邊形”(其定義為:多邊形內任意兩點所確定的線段都在多邊形內,由此也可以推導出,凸多邊形不能是空心的)。多邊形可以由其邊的端點(這裡可稱為頂點)來確定。(注意:如果使用的多邊形不是凸多邊形,則最後輸出的效果是未定義的——OpenGL為了效率,放寬了檢查,這可能導致顯示錯誤。要避免這個錯誤,盡量使用三角形,因為三角形都是凸多邊形)

可以想象,通過點、直線和多邊形,就可以組合成各種幾何圖形。甚至於,你可以把一段弧看成是很多短的直線段相連,這些直線段足夠短,以至於其長度小於一個像素的寬度。這樣一來弧和圓也可以表示出來了。通過位於不同平面的相連的小多邊形,我們還可以組成一個“曲面”。

二、在OpenGL中指定頂點
由以上的討論可以知道,“點”是一切的基礎。
如何指定一個點呢?OpenGL提供了一系列函數。它們都以glVertex開頭,後面跟一個數字和1~2個字母。例如:
glVertex2d
glVertex2f
glVertex3f
glVertex3fv
等等。
數字表示參數的個數,2表示有兩個參數,3表示三個,4表示四個(我知道有點羅嗦~)。
字母表示參數的類型,s表示16位整數(OpenGL中將這個類型定義為GLshort),
                   i表示32位整數(OpenGL中將這個類型定義為GLint和GLsizei),
                   f表示32位浮點數(OpenGL中將這個類型定義為GLfloat和GLclampf),
                   d表示64位浮點數(OpenGL中將這個類型定義為GLdouble和GLclampd)。
                   v表示傳遞的幾個參數將使用指針的方式,見下面的例子。
這些函數除了參數的類型和個數不同以外,功能是相同的。例如,以下五個代碼段的功能是等效的:
(一)glVertex2i(1, 3);
(二)glVertex2f(1.0f, 3.0f);
(三)glVertex3f(1.0f, 3.0f, 0.0f);
(四)glVertex4f(1.0f, 3.0f, 0.0f, 1.0f);
(五)GLfloat VertexArr3[] = {1.0f, 3.0f, 0.0f};
      glVertex3fv(VertexArr3);
以後我們將用glVertex*來表示這一系列函數。
注意:OpenGL的很多函數都是采用這樣的形式,一個相同的前綴再加上參數說明標記,這一點會隨著學習的深入而有更多的體會。

這裡擴展一下,在現在的編譯器中,能夠識別 5.f 相當於 5.0f. 這些技巧推薦用也推薦不用.看人吧. 例如 有時寫代碼覺得 C99/C11 風格好,

有時還是覺得C89的風格最穩妥.

舉例如下:

//C89
int hoge;
int piyo;

hoge = 16;
piyo = 1;
printf("hoge = %d, piyo = %d\n",hoge,piyo);


//C99 / C11

int hoge = 13;
printf("hoge = %d.\n",hoge);
int piyo = 5;
printf("heoo piyo is %d.\n",piyo);

繼續回到正題

三、開始繪制
假設現在我已經指定了若干頂點,那麼OpenGL是如何知道我想拿這些頂點來干什麼呢?是一個一個的畫出來,還是連成線?或者構成一個多邊形?或者做其它什麼事情?
為了解決這一問題,OpenGL要求:指定頂點的命令必須包含在glBegin函數之後,glEnd函數之前(否則指定的頂點將被忽略)。並由glBegin來指明如何使用這些點。
例如我寫:
glBegin(GL_POINTS);
     glVertex2f(0.0f, 0.0f);
     glVertex2f(0.5f, 0.0f);
glEnd();
則這兩個點將分別被畫出來。如果將GL_POINTS替換成GL_LINES,則兩個點將被認為是直線的兩個端點,OpenGL將會畫出一條直線。
我們還可以指定更多的頂點,然後畫出更復雜的圖形。
另一方面,glBegin支持的方式除了GL_POINTS和GL_LINES,還有GL_LINE_STRIP,GL_LINE_LOOP,GL_TRIANGLES,GL_TRIANGLE_STRIP,GL_TRIANGLE_FAN等.

這裡簡單對上面兩個函數總結如下:

 函數原型:
void glBegin(GLenum mode);
void glEnd(void);
參數說明:
  mode:創建圖元的類型。可以是以下數值
  GL_POINTS:把每一個頂點作為一個點進行處理,頂點n即定義了點n,共繪制N個點
  GL_LINES:把每一個頂點作為一個獨立的線段,頂點2n-1和2n之間共定義了n條線段,總共繪制N/2條線段
  GL_LINE_STRIP:繪制從第一個頂點到最後一個頂點依次相連的一組線段,第n和n+1個頂點定義了線段n,總共繪制n-1條線段
  GL_LINE_LOOP:繪制從第一個頂點到最後一個頂點依次相連的一組線段,然後最後一個頂點和第一個頂點相連,第n和n+1個頂點定義了線段n,總共繪制n條線段
  GL_TRIANGLES:把每個頂點作為一個獨立的三角形,頂點3n-2、3n-1和3n定義了第n個三角形,總共繪制N/3個三角形
  GL_TRIANGLE_STRIP:繪制一組相連的三角形,對於奇數n,頂點n、n+1和n+2定義了第n個三角形;對於偶數n,頂點n+1、n和n+2定義了第n個三角形,總共繪制N-2個三角形
  GL_TRIANGLE_FAN:繪制一組相連的三角形,三角形是由第一個頂點及其後給定的頂點確定,頂點1、n+1和n+2定義了第n個三角形,總共繪制N-2個三角形
  GL_QUADS:繪制由四個頂點組成的一組單獨的四邊形。頂點4n-3、4n-2、4n-1和4n定義了第n個四邊形。總共繪制N/4個四邊形
  GL_QUAD_STRIP:繪制一組相連的四邊形。每個四邊形是由一對頂點及其後給定的一對頂點共同確定的。頂點2n-1、2n、2n+2和2n+1定義了第n個四邊形,總共繪制N/2-1個四邊形
  GL_POLYGON:繪制一個凸多邊形。頂點1到n定義了這個多邊形。

我並不准備在glBegin的各種方式上大作文章。大家可以自己嘗試改變glBegin的方式和頂點的位置,生成一些有趣的圖案。
程序代碼:

void display(void)
{
     glClear(GL_COLOR_BUFFER_BIT);
     glBegin( /* 在這裡填上你所希望的模式 */ );
        /* 在這裡使用glVertex*系列函數 */
        /* 指定你所希望的頂點位置 */
     glEnd();
     glFlush();
}


把這段代碼改成你喜歡的樣子,然後用它替換第一課中的myDisplay函數,編譯後即可運行。

下面 我舉三個例子

先畫一個 圓,思路是打點,按照 圓的參數方程(r*cos t , r*sin t) ,t 屬於[0,2*Pi]

代碼如下:

#include <glut.h>
#define _USE_MATH_DEFINES //開啟 math.h中默認的常量宏
#include <math.h>

//繪制圓的頂點數
#define _INT_N (20)
/*
*這裡繪制 一個圓
*/
static void display(void)
{
    int i;
    GLdouble r = 0.5;

    glClear(GL_COLOR_BUFFER_BIT);

    glBegin(GL_POLYGON);
    for (i = 0;i < _INT_N;++i) {
        double p = 2 * M_PI * i / _INT_N;
        glVertex2d(r*cos(p),r*sin(p)); //打點連線
    }
    glEnd();

    glFlush();
}

int main(int argc, char *argv[])
{
    glutInit(&argc,argv);

    //glut 初始化 為 rgb顏色模式,單緩沖
    glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);

    //glut窗體創建
    glutInitWindowSize(500, 500);
    glutInitWindowPosition(123,456);
    glutCreateWindow("這裡測試圓的繪制");

   // 設置繪制函數
    glutDisplayFunc(display);
//glut窗體主循環 glutMainLoop(); return 0; }

按照上面代碼敲一遍,基本都明白了.這裡再說一些廢話,前提你需要搭好環境.搭環境完成後學習就已經完成了1/3

      學習編程技術 目前 總結 如下

    前線作戰 => 搭環境 hello world + 學習基礎api + 項目 => 可以進入戰斗模式了

           後線指揮 => 設計理念 + 抗壓和應急解決問題的實力 => 領跑者

 

第二個例子是畫一個五角星

實例圖如下:

畫這個 五角星 需要 你自己計算各個點的位置,需要初中幾何知識. 代碼參照如下:

#include <glut.h>
#define _USE_MATH_DEFINES //開啟 math.h中默認的常量宏
#include <math.h>

//繪制圓的頂點數
#define _INT_N (20)

//將角度值轉成弧度數
#define TO_F_PII(d) \
    (d*(float)M_PI/180)

/*
*這裡繪制 一個五角星
*/
static void display(void)
{
    //5x = 360 => x = 72度, x/2 = 36度 數學是不將中國道理的
    GLfloat a = 0.5f;//默認比例是一半 當前屏幕 opengl坐標系 以屏幕中央為(0,0)
    //計算 縱坐標 從上 到下 為 a , a*cos(72度) , -a*cos(36度)
    //計算的橫坐標 從左到右 為 -a*sin(72度) , -a*sin(36度), 0, a*sin(36度) , a*sin(72度)

    //A,B,C,D,E 坐標 依次 如下,需要 你自己計算 三角函數
    GLfloat pii72 = TO_F_PII(72.f);
    GLfloat pii36 = TO_F_PII(36.f);
    GLfloat
        pointA[2] = { -a*sinf(pii72), a*cosf(pii72) },
        pointB[2] = { 0.f , a },
        pointC[2] = { a*sinf(pii72) , a*cosf(pii72) },
        pointD[2] = { a*sinf(pii36) , -a*cosf(pii36)  },
        pointE[2] = { -a*sinf(pii36) , -a*cosf(pii36) };


    glClear(GL_COLOR_BUFFER_BIT);


    //從 A->C->E->B->D->A 順序一筆畫成
    glBegin(GL_LINE_LOOP);

    glVertex2fv(pointA);
    glVertex2fv(pointC);
    glVertex2fv(pointE);
    glVertex2fv(pointB);
    glVertex2fv(pointD);

    glEnd();

    glFlush();
}

五角星還是比較神聖的,可是自己已經污染了,熱愛家人吧.感謝他們給了我生命和一切.

下面是最後一個例子 繪制 f(x) = sinx , 這個例子是最簡單,做了一點修改是 坐標系擴大了一點,具體看下面代碼.

#define _USE_MATH_DEFINES //開啟 math.h中默認的常量宏
#include <math.h>

//繪制三角函數
static void display(void)
{
    GLfloat fz = 10.f,p;

    glClear(GL_COLOR_BUFFER_BIT);

    //先繪制坐標系
    glBegin(GL_LINES);
    glVertex2f(-1.f,0.f);
    glVertex2f(1.f,0.f);
    glVertex2f(0.f,1.f);
    glVertex2f(0.f,-1.f);
    glEnd();

    //繪制三角函數
    glBegin(GL_LINE_STRIP);
    for (p = -fz;p < fz;p += 0.01f)
        glVertex2f(p/fz,sinf(p)/fz);
    glEnd();

    glFlush();
}

到這裡關於 opengl 基礎學習的 第二節 基本完工了.上面例子很簡單,但需要 自己敲過才能知道一些. 這裡再說一些題外話.有沒有讀者對 上面 使用static 感到好奇.

這是為了在一個項目中 添加多個 main 文件 ,都用display 命名函數使用的技巧.因為 static 修飾的函數只在當前文件內有效,就不會出現 同名函數編譯不通過的問題.

關於static 和 extern 關鍵字 還是有很多學問的. 朋友們可以自己 科普一下.

 

最後說一點,當初寫博文之前,是實在不(ˇˍˇ) 想~看水文.後面自己寫了,才知道,好難.自己也成功的進入了 水筆行業. 對每一個寫博文的同行都表示 敬意.

做過,寫過,思考過,才知道 有些事 貴在堅持,難在 不想 坑人.

(有問題請隨時拍磚,馬上改)

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved