• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 15,  comments - 0,  trackbacks - 0
             

            OpenGL入門(mén)學(xué)習(xí)

            OpenGL作為當(dāng)前主流的圖形API之一,它在一些場(chǎng)合具有比DirectX更優(yōu)越的特性。

            1、與C語(yǔ)言緊密結(jié)合

            OpenGL命令最初就是用C語(yǔ)言函數(shù)來(lái)進(jìn)行描述的,對(duì)于學(xué)習(xí)過(guò)C語(yǔ)言的人來(lái)講,OpenGL是容易理解和學(xué)習(xí)的。如果你曾經(jīng)接觸過(guò)TCgraphics.h,你會(huì)發(fā)現(xiàn),使用OpenGL作圖甚至比TC更加簡(jiǎn)單

            2、強(qiáng)大的可移植性

            微軟的Direct3D雖然也是十分優(yōu)秀的圖形API,但它只用于Windows系統(tǒng)(現(xiàn)在還要加上一個(gè)XBOX游戲機(jī))。而OpenGL不僅用于 Windows,還可以用于Unix/Linux等其它系統(tǒng),它甚至在大型計(jì)算機(jī)、各種專業(yè)計(jì)算機(jī)(如:醫(yī)療用顯示設(shè)備)上都有應(yīng)用。并且,OpenGL 的基本命令都做到了硬件無(wú)關(guān),甚至是平臺(tái)無(wú)關(guān)。

            3、高性能的圖形渲染

            OpenGL是一個(gè)工業(yè)標(biāo)準(zhǔn),它的技術(shù)緊跟時(shí)代,現(xiàn)今各個(gè)顯卡廠家無(wú)一不對(duì)OpenGL提供強(qiáng)力支持,激烈的競(jìng)爭(zhēng)中使得OpenGL性能一直領(lǐng)先。

            總之,OpenGL是一個(gè)很NB的圖形軟件接口。至于究竟有多NB,去看看DOOM3QUAKE4等專業(yè)游戲就知道了。

            OpenGL官方網(wǎng)站(英文)

            http://www.opengl.org

             

            linux下編寫(xiě)OpenGL程序的一些準(zhǔn)備工作

            需要用到的軟件包有兩個(gè),glut tmake,分別可以從以下兩個(gè)網(wǎng)址下載:

            http://www.opengl.org/resources/libraries/glut/glut-3.7.tar.gz

            ftp://ftp.trolltech.com/freebies/tmake/tmake-1.8.tar.gz

            下載后的文件假設(shè)都放在/usr/src

            首先是安裝glut庫(kù),以下是從www.linux.com找到的編譯glut庫(kù)的手冊(cè)。

            Install GLUT 3.7 Distribution (optional)

            If you installed the MesaDemos/MesaLib package, then you have already installed GLUT 3.7 since it is included with MesaDemos. However, you may be interested in installing the GLUT

            manpages and you can skip right to the "Install GLUT manual pages", below ...

            Installing GLUT is a bit tricky. I'm not too familiar with imake, the program that it uses to manage the Makefiles, and didn't quite see how to get GLUT to install to where I wanted it (/usr/lib,

            but MesaDemos will do this without any trouble though). It can be done manually anyhow:

             cd /usr/src

             tar -xvzf glut-3.7.tar.gz

             cd glut-3.7

             Read the file: README.linux

             cd linux

             READ the file: README

             cp Glut.cf ..

             cd ..

             Edit Glut.cf: remove any Mesa references.

             Replace any -lMesaGL -lMesaGLU with -lGL -lGLU if needed.

             In particular, replace:

              OPENGL = $(TOP)/../lib/libMesaGL.so

              GLU = $(TOP)/../lib/libMesaGLU.so

             with:

              OPENGL = -lGL

              GLU = -lGLU

             ./mkmkfiles.imake

             cd lib/glut

             cp /usr/src/glut-3.7/linux/Makefile .

             Edit the Makefile: remove any Mesa references.

             Replace any -lMesaGL -lMesaGLU with -lGL -lGLU if needed.

             In particular, replace:

              OPENGL = $(TOP)/../lib/libMesaGL.so

              GLU = $(TOP)/../lib/libMesaGLU.so

             with:

              OPENGL = -lGL

              GLU = -lGLU

             make

             ln -s libglut.so.3.7 libglut.so

             ln -s libglut.so.3.7 libglut.so.3

             cp -d libglut.* /usr/lib

             cd ..

             cd gle

             # make a shared lib for libgle

             make

             gcc -shared -o libgle.so.3.7 *.o

             ln -s libgle.so.3.7 libgle.so

             ln -s libgle.so.3.7 libgle.so.3

             cp -d libgle.* /usr/lib

             cd ..

             cd mui

             # make a shared lib for libmui

             make

             gcc -shared -o libmui.so.3.7 *.o

             ln -s libmui.so.3.7 libmui.so

             ln -s libmui.so.3.7 libmui.so.3

             cp -d libmui.* /usr/lib

                    # Install the GLUT manual pages (not included with MesaDemos)

             cd /usr/src/glut-3.7

             make SUBDIRS=man Makefile

             cd man/glut

             make install.man

             ldconfig

             cd ../../progs/demos/ideas

             # edit the Makefile, change OPENGL = -lGL and GLU = -lGLU

             make

             ./ideas

             # test compiling some demos

             # take a look at which libraries have to be linked (-lX11 ...) in

             # the Makefiles.  Qt's tmake program available at www.troll.no

             # is a quick way to make a Makefile but you have to edit it

             # and add the -l needed.

            ideas如果運(yùn)行成功的話,說(shuō)明glut已經(jīng)可以用了。

            上面的幾步中,下載的glut包放在/usr/src目錄下,如果放在其他目錄下,將/usr/src改為相應(yīng)的目錄即可。

            此外應(yīng)該注意的是兩個(gè)Makefile文件的修改

              

            ·  OPENGL = $(TOP)/../lib/libMesaGL.so

              GLU = $(TOP)/../lib/libMesaGLU.so

             

              OPENGL = -lGL

              GLU = -lGLU

            因?yàn)樗付ǖ哪夸浿袥](méi)有libMesaGL.solibMesaGLU.so

            之后是tmake的配置,后面我們可以用它來(lái)生成pro工程文件和makefile文件。

            先將下載的tmake解壓縮,tar  -zxvf  tmake-1.8.tar.gz

            得到tmake-1.8目錄,之后設(shè)置兩個(gè)環(huán)境變量:PATHTMAKEPATH

            PATH=$PATH:/usr/src/tmake-1.8/bin

            export PATH

            TMAKEPATH=/usr/src/tmake-1.8/lib/linux-g++

            export TMAKEPATH

            新建一個(gè)測(cè)試目錄test,將glut-3.7目錄下的progs/redbook目錄下的hello.c復(fù)制到test目錄中

            之后生成一個(gè)pro文件:progen  -o hello.pro

            然后生成makefile文件:tmake hello.pro -o Makefile

            編輯生成的Makefile文件,在加載動(dòng)態(tài)連接庫(kù)的行里面加入 -lglut -lXi -lXmu

            保存,make

            ./hello 可以看到運(yùn)行結(jié)果就可以了。

             

            下面將對(duì)Windows下的OpenGL編程進(jìn)行簡(jiǎn)單介紹。

            學(xué)習(xí)OpenGL前的準(zhǔn)備工作

            第一步,選擇一個(gè)編譯環(huán)境

            現(xiàn)在Windows系統(tǒng)的主流編譯環(huán)境有Visual StudioBroland C++ BuilderDev-C++等,它們都是支持OpenGL的。但這里我們選擇Visual Studio 2005作為學(xué)習(xí)OpenGL的環(huán)境。

            第二步,安裝GLUT工具包

            GLUT不是OpenGL所必須的,但它會(huì)給我們的學(xué)習(xí)帶來(lái)一定的方便,推薦安裝。

            Windows VS2005環(huán)境下環(huán)境下安裝GLUT的步驟:

            1、將下載的壓縮包解開(kāi),將得到5個(gè)文件

            2、在我的電腦中搜索“gl.h”,并找到其所在文件夾(如果是VisualStudio2005,則應(yīng)該是其安裝目錄下面的“VC\PlatformSDK\include\gl文件夾)。把解壓得到的glut.h放到這個(gè)文件夾。

            3、把解壓得到的glut.libglut32.lib放到靜態(tài)函數(shù)庫(kù)所在文件夾(如果是VisualStudio2005,則應(yīng)該是其安裝目錄下面的“VC\lib”文件夾)。

            4、把解壓得到的glut.dllglut32.dll放到操作系統(tǒng)目錄下面的system32文件夾內(nèi)。(典型的位置為:C:\Windows\System32

             

            Windows VS2008環(huán)境下環(huán)境下安裝GLUT的步驟:

            如果是VisualStudio2008,把解壓得到的glut.h放到“Microsoft Visual Studio 9.0\VC\include”;

            如果是VisualStudio2008,把解壓得到的glut.dllglut32.dll放到“Microsoft Visual Studio 9.0\VC\lib”。

             

            第三步,建立一個(gè)OpenGL工程

            這里以VisualStudio2005為例。

            選擇File->New->Project,然后選擇Win32 Console Application,選擇一個(gè)名字,然后按OK

            在談出的對(duì)話框左邊點(diǎn)Application Settings,找到Empty project并勾上,選擇Finish

            然后向該工程添加一個(gè)代碼文件,取名為“OpenGL.c”,注意用.c來(lái)作為文件結(jié)尾。

            搞定了,就跟平時(shí)的工程沒(méi)什么兩樣的。

             

            第一個(gè)OpenGL程序

            一個(gè)簡(jiǎn)單的OpenGL程序如下:(注意,如果需要編譯并運(yùn)行,需要正確安裝GLUT,安裝方法如上所述)

            #include <GL/glut.h>

            void myDisplay(void)

            {

                glClear(GL_COLOR_BUFFER_BIT);

                glRectf(-0.5f, -0.5f, 0.5f, 0.5f);

                glFlush();

            }

             

            int main(int argc, char *argv[])

            {

                glutInit(&argc, argv);

                glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);

                glutInitWindowPosition(100, 100);

                glutInitWindowSize(400, 400);

                glutCreateWindow("第一個(gè)OpenGL程序");

                glutDisplayFunc(&myDisplay);

                glutMainLoop();

                return 0;

            }

             

            該程序的作用是在一個(gè)黑色的窗口中央畫(huà)一個(gè)白色的矩形。下面對(duì)各行語(yǔ)句進(jìn)行說(shuō)明。

            首先,需要包含頭文件#include <GL/glut.h>,這是GLUT的頭文件。

            本來(lái)OpenGL程序一般還要包含<GL/gl.h><GL/glu.h>,但GLUT的頭文件中已經(jīng)自動(dòng)將這兩個(gè)文件包含了,不必再次包含。

             

            然后看main函數(shù)。

            int main(int argc, char *argv[]),這個(gè)是帶命令行參數(shù)的main函數(shù),各位應(yīng)該見(jiàn)過(guò)吧?沒(méi)見(jiàn)過(guò)的同志們請(qǐng)多翻翻書(shū),等弄明白了再往下看。

            注意main函數(shù)中的各語(yǔ)句,除了最后的return之外,其余全部以glut開(kāi)頭。這種以glut開(kāi)頭的函數(shù)都是GLUT工具包所提供的函數(shù),下面對(duì)用到的幾個(gè)函數(shù)進(jìn)行介紹。

            1glutInit,對(duì)GLUT進(jìn)行初始化,這個(gè)函數(shù)必須在其它的GLUT使用之前調(diào)用一次。其格式比較死板,一般照抄這句glutInit(&argc, argv)就可以了。

            2 glutInitDisplayMode,設(shè)置顯示方式,其中GLUT_RGB表示使用RGB顏色,與之對(duì)應(yīng)的還有GLUT_INDEX(表示使用索引顏色)。GLUT_SINGLE表示使用單緩沖,與之對(duì)應(yīng)的還有GLUT_DOUBLE(使用雙緩沖)。更多信息,請(qǐng)自己Google。當(dāng)然以后的教程也會(huì)有一些講解。

            3glutInitWindowPosition,這個(gè)簡(jiǎn)單,設(shè)置窗口在屏幕中的位置。

            4glutInitWindowSize,這個(gè)也簡(jiǎn)單,設(shè)置窗口的大小。

            5glutCreateWindow,根據(jù)前面設(shè)置的信息創(chuàng)建窗口。參數(shù)將被作為窗口的標(biāo)題。注意:窗口被創(chuàng)建后,并不立即顯示到屏幕上。需要調(diào)用glutMainLoop才能看到窗口。

            6glutDisplayFunc,設(shè)置一個(gè)函數(shù),當(dāng)需要進(jìn)行畫(huà)圖時(shí),這個(gè)函數(shù)就會(huì)被調(diào)用。(這個(gè)說(shuō)法不夠準(zhǔn)確,但準(zhǔn)確的說(shuō)法可能初學(xué)者不太好理解,暫時(shí)這樣說(shuō)吧)。

            7glutMainLoop,進(jìn)行一個(gè)消息循環(huán)。(這個(gè)可能初學(xué)者也不太明白,現(xiàn)在只需要知道這個(gè)函數(shù)可以顯示窗口,并且等待窗口關(guān)閉后才會(huì)返回,這就足夠了。)

             

            glutDisplayFunc函數(shù)中,我們?cè)O(shè)置了當(dāng)需要畫(huà)圖時(shí),請(qǐng)調(diào)用myDisplay函數(shù)。于是myDisplay函數(shù)就用來(lái)畫(huà)圖。觀察myDisplay中的三個(gè)函數(shù)調(diào)用,發(fā)現(xiàn)它們都以gl開(kāi)頭。這種以gl開(kāi)頭的函數(shù)都是OpenGL的標(biāo)準(zhǔn)函數(shù),下面對(duì)用到的函數(shù)進(jìn)行介紹。

            1glClear,清除。GL_COLOR_BUFFER_BIT表示清除顏色,glClear函數(shù)還可以清除其它的東西,但這里不作介紹。

            2glRectf,畫(huà)一個(gè)矩形。四個(gè)參數(shù)分別表示了位于對(duì)角線上的兩個(gè)點(diǎn)的橫、縱坐標(biāo)。

            3glFlush,保證前面的OpenGL命令立即執(zhí)行(而不是讓它們?cè)诰彌_區(qū)中等待)。其作用跟fflush(stdout)類似。


             

             

            OpenGL入門(mén)學(xué)習(xí)(二)(轉(zhuǎn))

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

             

            一、點(diǎn)、直線和多邊形

            我們知道數(shù)學(xué)(具體的說(shuō),是幾何學(xué))中有點(diǎn)、直線和多邊形的概念,但這些概念在計(jì)算機(jī)中會(huì)有所不同。

            數(shù)學(xué)上的點(diǎn),只有位置,沒(méi)有大小。但在計(jì)算機(jī)中,無(wú)論計(jì)算精度如何提高,始終不能表示一個(gè)無(wú)窮小的點(diǎn)。另一方面,無(wú)論圖形輸出設(shè)備(例如,顯示器)如何精確,始終不能輸出一個(gè)無(wú)窮小的點(diǎn)。一般情況下,OpenGL中的點(diǎn)將被畫(huà)成單個(gè)的像素(像素的概念,請(qǐng)自己搜索之~),雖然它可能足夠小,但并不會(huì)是無(wú)窮小。同一像素上,OpenGL可以繪制許多坐標(biāo)只有稍微不同的點(diǎn),但該像素的具體顏色將取決于OpenGL的實(shí)現(xiàn)。當(dāng)然,過(guò)度的注意細(xì)節(jié)就是鉆牛角尖,我們大可不必花費(fèi)過(guò)多的精力去研究多個(gè)點(diǎn)如何畫(huà)到同一像素上

            同樣的,數(shù)學(xué)上的直線沒(méi)有寬度,但OpenGL的直線則是有寬度的。同時(shí),OpenGL的直線必須是有限長(zhǎng)度,而不是像數(shù)學(xué)概念那樣是無(wú)限的。可以認(rèn)為,OpenGL直線概念與數(shù)學(xué)上的線段接近,它可以由兩個(gè)端點(diǎn)來(lái)確定。

            多邊形是由多條線段首尾相連而形成的閉合區(qū)域。OpenGL規(guī)定,一個(gè)多邊形必須是一個(gè)凸多邊形(其定義為:多邊形內(nèi)任意兩點(diǎn)所確定的線段都在多邊形內(nèi),由此也可以推導(dǎo)出,凸多邊形不能是空心的)。多邊形可以由其邊的端點(diǎn)(這里可稱為頂點(diǎn))來(lái)確定。(注意:如果使用的多邊形不是凸多邊形,則最后輸出的效果是未定義的——OpenGL為了效率,放寬了檢查,這可能導(dǎo)致顯示錯(cuò)誤。要避免這個(gè)錯(cuò)誤,盡量使用三角形,因?yàn)槿切味际峭苟噙呅危?/span>

             

            可以想象,通過(guò)點(diǎn)、直線和多邊形,就可以組合成各種幾何圖形。甚至于,你可以把一段弧看成是很多短的直線段相連,這些直線段足夠短,以至于其長(zhǎng)度小于一個(gè)像素的寬度。這樣一來(lái)弧和圓也可以表示出來(lái)了。通過(guò)位于不同平面的相連的小多邊形,我們還可以組成一個(gè)曲面

             

            二、在OpenGL中指定頂點(diǎn)

            由以上的討論可以知道,點(diǎn)是一切的基礎(chǔ)。

            如何指定一個(gè)點(diǎn)呢?OpenGL提供了一系列函數(shù)。它們都以glVertex開(kāi)頭,后面跟一個(gè)數(shù)字和1~2個(gè)字母。例如:

            glVertex2d

            glVertex2f

            glVertex3f

            glVertex3fv

            等等。

            數(shù)字表示參數(shù)的個(gè)數(shù),2表示有兩個(gè)參數(shù),3表示三個(gè),4表示四個(gè)(我知道有點(diǎn)羅嗦~)。

            字母表示參數(shù)的類型,s表示16位整數(shù)(OpenGL中將這個(gè)類型定義為GLshort),

                              i表示32位整數(shù)(OpenGL中將這個(gè)類型定義為GLintGLsizei),

                              f表示32位浮點(diǎn)數(shù)(OpenGL中將這個(gè)類型定義為GLfloatGLclampf),

                              d表示64位浮點(diǎn)數(shù)(OpenGL中將這個(gè)類型定義為GLdoubleGLclampd)。

                              v表示傳遞的幾個(gè)參數(shù)將使用指針的方式,見(jiàn)下面的例子。

            這些函數(shù)除了參數(shù)的類型和個(gè)數(shù)不同以外,功能是相同的。例如,以下五個(gè)代碼段的功能是等效的:

            (一)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*來(lái)表示這一系列函數(shù)。

            注意:OpenGL的很多函數(shù)都是采用這樣的形式,一個(gè)相同的前綴再加上參數(shù)說(shuō)明標(biāo)記,這一點(diǎn)會(huì)隨著學(xué)習(xí)的深入而有更多的體會(huì)。

             

             

            三、開(kāi)始繪制

            假設(shè)現(xiàn)在我已經(jīng)指定了若干頂點(diǎn),那么OpenGL是如何知道我想拿這些頂點(diǎn)來(lái)干什么呢?是一個(gè)一個(gè)的畫(huà)出來(lái),還是連成線?或者構(gòu)成一個(gè)多邊形?或者做其它什么事情?

            為了解決這一問(wèn)題,OpenGL要求:指定頂點(diǎn)的命令必須包含在glBegin函數(shù)之后,glEnd函數(shù)之前(否則指定的頂點(diǎn)將被忽略)。并由glBegin來(lái)指明如何使用這些點(diǎn)。

            例如我寫(xiě):

            glBegin(GL_POINTS);

                glVertex2f(0.0f, 0.0f);

                glVertex2f(0.5f, 0.0f);

            glEnd();

            則這兩個(gè)點(diǎn)將分別被畫(huà)出來(lái)。如果將GL_POINTS替換成GL_LINES,則兩個(gè)點(diǎn)將被認(rèn)為是直線的兩個(gè)端點(diǎn),OpenGL將會(huì)畫(huà)出一條直線。

            我們還可以指定更多的頂點(diǎn),然后畫(huà)出更復(fù)雜的圖形。

            另一方面,glBegin支持的方式除了GL_POINTSGL_LINES,還有GL_LINE_STRIPGL_LINE_LOOPGL_TRIANGLESGL_TRIANGLE_STRIPGL_TRIANGLE_FAN等,每種方式的大致效果見(jiàn)下圖:

            我并不準(zhǔn)備在glBegin的各種方式上大作文章。大家可以自己嘗試改變glBegin的方式和頂點(diǎn)的位置,生成一些有趣的圖案。

             

            程序代碼:

            void myDisplay(void)

            {

                glClear(GL_COLOR_BUFFER_BIT);

                glBegin( /* 在這里填上你所希望的模式 */ );

                    /* 在這里使用glVertex*系列函數(shù) */

                    /* 指定你所希望的頂點(diǎn)位置 */

                glEnd();

                glFlush();

            }

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

             

            兩個(gè)例子

            例一、畫(huà)一個(gè)圓

            /*

            正四邊形,正五邊形,正六邊形,……,直到正n邊形,當(dāng)n越大時(shí),這個(gè)圖形就越接近圓

            當(dāng)n大到一定程度后,人眼將無(wú)法把它跟真正的圓相區(qū)別

            這時(shí)我們已經(jīng)成功的畫(huà)出了一個(gè)

            (注:畫(huà)圓的方法很多,這里使用的是比較簡(jiǎn)單,但效率較低的一種)

            試修改下面的const int n的值,觀察當(dāng)n=3,4,5,8,10,15,20,30,50等不同數(shù)值時(shí)輸出的變化情況

            GL_POLYGON改為GL_LINE_LOOPGL_POINTS等其它方式,觀察輸出的變化情況

            */

            #include <math.h>

            const int n = 20;

            const GLfloat R = 0.5f;

            const GLfloat Pi = 3.1415926536f;

            void myDisplay(void)

            {

                int i;

                glClear(GL_COLOR_BUFFER_BIT);

                glBegin(GL_POLYGON);

                for(i=0; i<n; ++i)

                    glVertex2f(R*cos(2*Pi/n*i), R*sin(2*Pi/n*i));

                glEnd();

                glFlush();

            }

             

             

            例二、畫(huà)一個(gè)五角星

            /*

            設(shè)五角星的五個(gè)頂點(diǎn)分布位置關(guān)系如下:

                 A

             E       B

             

               D   C

            首先,根據(jù)余弦定理列方程,計(jì)算五角星的中心到頂點(diǎn)的距離a

            (假設(shè)五角星對(duì)應(yīng)正五邊形的邊長(zhǎng)為.0

            a = 1 / (2-2*cos(72*Pi/180));

            然后,根據(jù)正弦和余弦的定義,計(jì)算Bx坐標(biāo)bxy坐標(biāo)by,以及Cy坐標(biāo)

            (假設(shè)五角星的中心在坐標(biāo)原點(diǎn))

            bx = a * cos(18 * Pi/180);

            by = a * sin(18 * Pi/180);

            cy = -a * cos(18 * Pi/180);

            五個(gè)點(diǎn)的坐標(biāo)就可以通過(guò)以上四個(gè)量和一些常數(shù)簡(jiǎn)單的表示出來(lái)

            */

            #include <math.h>

            const GLfloat Pi = 3.1415926536f;

            void myDisplay(void)

            {

                GLfloat a = 1 / (2-2*cos(72*Pi/180));

                GLfloat bx = a * cos(18 * Pi/180);

                GLfloat by = a * sin(18 * Pi/180);

                GLfloat cy = -a * cos(18 * Pi/180);

                GLfloat

                    PointA[2] = { 0, a },

                    PointB[2] = { bx, by },

                    PointC[2] = { 0.5, cy },

                    PointD[2] = { -0.5, cy },

                    PointE[2] = { -bx, by };

             

                glClear(GL_COLOR_BUFFER_BIT);

                // 按照A->C->E->B->D->A的順序,可以一筆將五角星畫(huà)出

                glBegin(GL_LINE_LOOP);

                    glVertex2fv(PointA);

                    glVertex2fv(PointC);

                    glVertex2fv(PointE);

                    glVertex2fv(PointB);

                    glVertex2fv(PointD);

                glEnd();

                glFlush();

            }

             

             

            例三、畫(huà)出正弦函數(shù)的圖形

            /*

            由于OpenGL默認(rèn)坐標(biāo)值只能從-11,(可以修改,但方法留到以后講)

            所以我們?cè)O(shè)置一個(gè)因子factor,把所有的坐標(biāo)值等比例縮小,

            這樣就可以畫(huà)出更多個(gè)正弦周期

            試修改factor的值,觀察變化情況

            */

            #include <math.h>

            const GLfloat factor = 0.1f;

            void myDisplay(void)

            {

                GLfloat x;

                glClear(GL_COLOR_BUFFER_BIT);

                glBegin(GL_LINES);

                    glVertex2f(-1.0f, 0.0f);

                    glVertex2f(1.0f, 0.0f);        // 以上兩個(gè)點(diǎn)可以畫(huà)x

                    glVertex2f(0.0f, -1.0f);

                    glVertex2f(0.0f, 1.0f);        // 以上兩個(gè)點(diǎn)可以畫(huà)y

                glEnd();

                glBegin(GL_LINE_STRIP);

                for(x=-1.0f/factor; x<1.0f/factor; x+=0.01f)

                {

                    glVertex2f(x*factor, sin(x)*factor);

                }

                glEnd();

                glFlush();

            }

             

             

            小結(jié)

            本課講述了點(diǎn)、直線和多邊形的概念,以及如何使用OpenGL來(lái)描述點(diǎn),并使用點(diǎn)來(lái)描述幾何圖形。

            大家可以發(fā)揮自己的想象,畫(huà)出各種幾何圖形,當(dāng)然,也可以用GL_LINE_STRIP把很多位置相近的點(diǎn)連接起來(lái),構(gòu)成函數(shù)圖象。如果有興趣,也可以去找一些圖象比較美觀的函數(shù),自己動(dòng)手,用OpenGL把它畫(huà)出來(lái)。

             

            =====================   第二課    =====================

            OpenGL入門(mén)學(xué)習(xí)(三)(轉(zhuǎn))

            在第二課中,我們學(xué)習(xí)了如何繪制幾何圖形,但大家如果多寫(xiě)幾個(gè)程序,就會(huì)發(fā)現(xiàn)其實(shí)還是有些郁悶之處。例如:點(diǎn)太小,難以看清楚;直線也太細(xì),不舒服;或者想畫(huà)虛線,但不知道方法只能用許多短直線,甚至用點(diǎn)組合而成。

            這些問(wèn)題將在本課中被解決。

             

            下面就點(diǎn)、直線、多邊形分別討論。

             

             

            1、關(guān)于點(diǎn)

            點(diǎn)的大小默認(rèn)為1個(gè)像素,但也可以改變之。改變的命令為glPointSize,其函數(shù)原型如下:

            void glPointSize(GLfloat size);

            size必須大于0.0f,默認(rèn)值為1.0f,單位為像素

            注意:對(duì)于具體的OpenGL實(shí)現(xiàn),點(diǎn)的大小都有個(gè)限度的,如果設(shè)置的size超過(guò)最大值,則設(shè)置可能會(huì)有問(wèn)題。

            例子:

            void myDisplay(void)

            {

                glClear(GL_COLOR_BUFFER_BIT);

                glPointSize(5.0f);

                glBegin(GL_POINTS);

                    glVertex2f(0.0f, 0.0f);

                    glVertex2f(0.5f, 0.5f);

                glEnd();

                glFlush();

            }

             

            2、關(guān)于直線

            1)直線可以指定寬度:

            void glLineWidth(GLfloat width);

            其用法跟glPointSize類似。

            2)畫(huà)虛線。

            首先,使用glEnable(GL_LINE_STIPPLE);來(lái)啟動(dòng)虛線模式(使用glDisable(GL_LINE_STIPPLE)可以關(guān)閉之)。

            然后,使用glLineStipple來(lái)設(shè)置虛線的樣式。

            void glLineStipple(GLint factor, GLushort pattern);

            pattern是由10組成的長(zhǎng)度為16的序列,從最低位開(kāi)始看,如果為1,則直線上接下來(lái)應(yīng)該畫(huà)的factor個(gè)點(diǎn)將被畫(huà)為實(shí)的;如果為0,則直線上接下來(lái)應(yīng)該畫(huà)的factor個(gè)點(diǎn)將被畫(huà)為虛的。

            以下是一些例子:

            聲明:該圖片來(lái)自www.opengl.org,該圖片是《OpenGL編程指南》一書(shū)的附圖,由于該書(shū)的舊版(第一版,1994年)已經(jīng)流傳于網(wǎng)絡(luò),我希望沒(méi)有觸及到版權(quán)問(wèn)題。

            示例代碼:

            void myDisplay(void)

            {

                glClear(GL_COLOR_BUFFER_BIT);

                glEnable(GL_LINE_STIPPLE);

                glLineStipple(2, 0x0F0F);

                glLineWidth(10.0f);

                glBegin(GL_LINES);

                    glVertex2f(0.0f, 0.0f);

                    glVertex2f(0.5f, 0.5f);

                glEnd();

                glFlush();

            }

             

             

            3、關(guān)于多邊形

            多邊形的內(nèi)容較多,我們將講述以下四個(gè)方面。

            1)多邊形的兩面以及繪制方式。

            雖然我們目前還沒(méi)有真正的使用三維坐標(biāo)來(lái)畫(huà)圖,但是建立一些三維的概念還是必要的。

            從三維的角度來(lái)看,一個(gè)多邊形具有兩個(gè)面。每一個(gè)面都可以設(shè)置不同的繪制方式:填充、只繪制邊緣輪廓線、只繪制頂點(diǎn),其中填充是默認(rèn)的方式。可以為兩個(gè)面分別設(shè)置不同的方式。

            glPolygonMode(GL_FRONT, GL_FILL);           // 設(shè)置正面為填充方式

            glPolygonMode(GL_BACK, GL_LINE);            // 設(shè)置反面為邊緣繪制方式

            glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); // 設(shè)置兩面均為頂點(diǎn)繪制方式

             

             

            2)反轉(zhuǎn)

            一般約定為頂點(diǎn)以逆時(shí)針順序出現(xiàn)在屏幕上的面正面,另一個(gè)面即成為反面。生活中常見(jiàn)的物體表面,通常都可以用這樣的正面反面合理的被表現(xiàn)出來(lái)(請(qǐng)找一個(gè)比較透明的礦泉水瓶子,在正對(duì)你的一面沿逆時(shí)針畫(huà)一個(gè)圓,并標(biāo)明畫(huà)的方向,然后將背面轉(zhuǎn)為正面,畫(huà)一個(gè)類似的圓,體會(huì)一下正面反面。你會(huì)發(fā)現(xiàn)正對(duì)你的方向,瓶的外側(cè)是正面,而背對(duì)你的方向,瓶的內(nèi)側(cè)才是正面。正對(duì)你的內(nèi)側(cè)和背對(duì)你的外側(cè)則是反面。這樣一來(lái),同樣屬于瓶的外側(cè)這個(gè)表面,但某些地方算是正面,某些地方卻算是反面了)。

            但也有一些表面比較特殊。例如麥比烏斯帶(請(qǐng)自己Google一下),可以全部使用正面或全部使用背面來(lái)表示。

            可以通過(guò)glFrontFace函數(shù)來(lái)交換正面反面的概念。

            glFrontFace(GL_CCW);  // 設(shè)置CCW方向?yàn)?/span>正面CCWCounterClockWise,逆時(shí)針

            glFrontFace(GL_CW);   // 設(shè)置CW方向?yàn)?/span>正面CWClockWise,順時(shí)針

            下面是一個(gè)示例程序,請(qǐng)用它替換第一課中的myDisplay函數(shù),并將glFrontFace(GL_CCW)修改為glFrontFace(GL_CW),并觀察結(jié)果的變化。

            void myDisplay(void)

            {

                glClear(GL_COLOR_BUFFER_BIT);

                glPolygonMode(GL_FRONT, GL_FILL); // 設(shè)置正面為填充模式

                glPolygonMode(GL_BACK, GL_LINE);  // 設(shè)置反面為線形模式

                glFrontFace(GL_CCW);              // 設(shè)置逆時(shí)針?lè)较驗(yàn)檎?/span>

                glBegin(GL_POLYGON);              // 按逆時(shí)針繪制一個(gè)正方形,在左下方

                    glVertex2f(-0.5f, -0.5f);

                    glVertex2f(0.0f, -0.5f);

                    glVertex2f(0.0f, 0.0f);

                    glVertex2f(-0.5f, 0.0f);

                glEnd();

                glBegin(GL_POLYGON);              // 按順時(shí)針繪制一個(gè)正方形,在右上方

                    glVertex2f(0.0f, 0.0f);

                    glVertex2f(0.0f, 0.5f);

                    glVertex2f(0.5f, 0.5f);

                    glVertex2f(0.5f, 0.0f);

                glEnd();

                glFlush();

            }

            3)剔除多邊形表面

            在三維空間中,一個(gè)多邊形雖然有兩個(gè)面,但我們無(wú)法看見(jiàn)背面的那些多邊形,而一些多邊形雖然是正面的,但被其他多邊形所遮擋。如果將無(wú)法看見(jiàn)的多邊形和可見(jiàn)的多邊形同等對(duì)待,無(wú)疑會(huì)降低我們處理圖形的效率。在這種時(shí)候,可以將不必要的面剔除。

            首先,使用glEnable(GL_CULL_FACE);來(lái)啟動(dòng)剔除功能(使用glDisable(GL_CULL_FACE)可以關(guān)閉之)

            然后,使用glCullFace來(lái)進(jìn)行剔除。

            glCullFace的參數(shù)可以是GL_FRONTGL_BACK或者GL_FRONT_AND_BACK,分別表示剔除正面、剔除反面、剔除正反兩面的多邊形。

            注意:剔除功能只影響多邊形,而對(duì)點(diǎn)和直線無(wú)影響。例如,使用glCullFace(GL_FRONT_AND_BACK)后,所有的多邊形都將被剔除,所以看見(jiàn)的就只有點(diǎn)和直線。

             

            4)鏤空多邊形

            直線可以被畫(huà)成虛線,而多邊形則可以進(jìn)行鏤空。

            首先,使用glEnable(GL_POLYGON_STIPPLE);來(lái)啟動(dòng)鏤空模式(使用glDisable(GL_POLYGON_STIPPLE)可以關(guān)閉之)。

            然后,使用glPolygonStipple來(lái)設(shè)置鏤空的樣式。

            void glPolygonStipple(const GLubyte *mask);

            其中的參數(shù)mask指向一個(gè)長(zhǎng)度為128字節(jié)的空間,它表示了一個(gè)32*32的矩形應(yīng)該如何鏤空。其中:第一個(gè)字節(jié)表示了最左下方的從左到右(也可以是從右到左,這個(gè)可以修改)8個(gè)像素是否鏤空(1表示不鏤空,顯示該像素;0表示鏤空,顯示其后面的顏色),最后一個(gè)字節(jié)表示了最右上方的8個(gè)像素是否鏤空。

            但是,如果我們直接定義這個(gè)mask數(shù)組,像這樣:

            static GLubyte Mask[128] =

            {

                0x00, 0x00, 0x00, 0x00,   //  這是最下面的一行

                0x00, 0x00, 0x00, 0x00,

                0x03, 0x80, 0x01, 0xC0,   //  

                0x06, 0xC0, 0x03, 0x60,   //  

                0x04, 0x60, 0x06, 0x20,   //  

                0x04, 0x30, 0x0C, 0x20,   //  

                0x04, 0x18, 0x18, 0x20,   //  

                0x04, 0x0C, 0x30, 0x20,   //  

                0x04, 0x06, 0x60, 0x20,   //  

                0x44, 0x03, 0xC0, 0x22,   //  

                0x44, 0x01, 0x80, 0x22,   //  

                0x44, 0x01, 0x80, 0x22,   //  

                0x44, 0x01, 0x80, 0x22,   //  使

                0x44, 0x01, 0x80, 0x22,   //  

                0x44, 0x01, 0x80, 0x22,

                0x44, 0x01, 0x80, 0x22,

                0x66, 0x01, 0x80, 0x66,

                0x33, 0x01, 0x80, 0xCC,

                0x19, 0x81, 0x81, 0x98,

                0x0C, 0xC1, 0x83, 0x30,

                0x07, 0xE1, 0x87, 0xE0,

                0x03, 0x3F, 0xFC, 0xC0,

                0x03, 0x31, 0x8C, 0xC0,

                0x03, 0x3F, 0xFC, 0xC0,

                0x06, 0x64, 0x26, 0x60,

                0x0C, 0xCC, 0x33, 0x30,

                0x18, 0xCC, 0x33, 0x18,

                0x10, 0xC4, 0x23, 0x08,

                0x10, 0x63, 0xC6, 0x08,

                0x10, 0x30, 0x0C, 0x08,

                0x10, 0x18, 0x18, 0x08,

                0x10, 0x00, 0x00, 0x08   // 這是最上面的一行

            };

            這樣一堆數(shù)據(jù)非常缺乏直觀性,我們需要很費(fèi)勁的去分析,才會(huì)發(fā)現(xiàn)它表示的竟然是一只蒼蠅。

            如果將這樣的數(shù)據(jù)保存成圖片,并用專門(mén)的工具進(jìn)行編輯,顯然會(huì)方便很多。下面介紹如何做到這一點(diǎn)。

             

             

            首先,用Windows自帶的畫(huà)筆程序新建一副圖片,取名為mask.bmp,注意保存時(shí),應(yīng)該選擇單色位圖。在圖象”->“屬性對(duì)話框中,設(shè)置圖片的高度和寬度均為32

            用放大鏡觀察圖片,并編輯之。黑色對(duì)應(yīng)二進(jìn)制零(鏤空),白色對(duì)應(yīng)二進(jìn)制一(不鏤空),編輯完畢后保存。

            然后,就可以使用以下代碼來(lái)獲得這個(gè)Mask數(shù)組了。

            static GLubyte Mask[128];

            FILE *fp;

            fp = fopen("mask.bmp", "rb");

            if( !fp )

                exit(0);

            // 移動(dòng)文件指針到這個(gè)位置,使得再讀sizeof(Mask)個(gè)字節(jié)就會(huì)遇到文件結(jié)束

            // 注意-(int)sizeof(Mask)雖然不是什么好的寫(xiě)法,但這里它確實(shí)是正確有效的

            // 如果直接寫(xiě)-sizeof(Mask)的話,因?yàn)?/span>sizeof取得的是一個(gè)無(wú)符號(hào)數(shù),取負(fù)號(hào)會(huì)有問(wèn)題

            if( fseek(fp, -(int)sizeof(Mask), SEEK_END) )

                exit(0);

            // 讀取sizeof(Mask)個(gè)字節(jié)到Mask

            if( !fread(Mask, sizeof(Mask), 1, fp) )

                exit(0);

            fclose(fp);

             

             

            好的,現(xiàn)在請(qǐng)自己編輯一個(gè)圖片作為mask,并用上述方法取得Mask數(shù)組,運(yùn)行后觀察效果。

            說(shuō)明:繪制虛線時(shí)可以設(shè)置factor因子,但多邊形的鏤空無(wú)法設(shè)置factor因子。請(qǐng)用鼠標(biāo)改變窗口的大小,觀察鏤空效果的變化情況。

            #include <stdio.h>

            #include <stdlib.h>

            void myDisplay(void)

            {

                static GLubyte Mask[128];

                FILE *fp;

                fp = fopen("mask.bmp", "rb");

                if( !fp )

                    exit(0);

                if( fseek(fp, -(int)sizeof(Mask), SEEK_END) )

                    exit(0);

                if( !fread(Mask, sizeof(Mask), 1, fp) )

                    exit(0);

                fclose(fp);

                glClear(GL_COLOR_BUFFER_BIT);

                glEnable(GL_POLYGON_STIPPLE);

                glPolygonStipple(Mask);

                glRectf(-0.5f, -0.5f, 0.0f, 0.0f);  // 在左下方繪制一個(gè)有鏤空效果的正方形

                glDisable(GL_POLYGON_STIPPLE);

                glRectf(0.0f, 0.0f, 0.5f, 0.5f);    // 在右上方繪制一個(gè)無(wú)鏤空效果的正方形

                glFlush();

            }

             

             

            小結(jié)

            本課學(xué)習(xí)了繪制幾何圖形的一些細(xì)節(jié)。

            點(diǎn)可以設(shè)置大小。

            直線可以設(shè)置寬度;可以將直線畫(huà)成虛線。

            多邊形的兩個(gè)面的繪制方法可以分別設(shè)置;在三維空間中,不可見(jiàn)的多邊形可以被剔除;可以將填充多邊形繪制成鏤空的樣式。

            了解這些細(xì)節(jié)會(huì)使我們?cè)谝恍﹫D象繪制中更加得心應(yīng)手。

            另外,把一些數(shù)據(jù)寫(xiě)到程序之外的文件中,并用專門(mén)的工具編輯之,有時(shí)可以顯得更方便。

             

            =====================   第三課    =====================

            OpenGL入門(mén)學(xué)習(xí)(四)(轉(zhuǎn))

            本次學(xué)習(xí)的是顏色的選擇。終于要走出黑白的世界了~~

             

             

            OpenGL支持兩種顏色模式:一種是RGBA,一種是顏色索引模式。

            無(wú)論哪種顏色模式,計(jì)算機(jī)都必須為每一個(gè)像素保存一些數(shù)據(jù)。不同的是,RGBA模式中,數(shù)據(jù)直接就代表了顏色;而顏色索引模式中,數(shù)據(jù)代表的是一個(gè)索引,要得到真正的顏色,還必須去查索引表。

             

            1. RGBA顏色

            RGBA模式中,每一個(gè)像素會(huì)保存以下數(shù)據(jù):R值(紅色分量)、G值(綠色分量)、B值(藍(lán)色分量)和A值(alpha分量)。其中紅、綠、藍(lán)三種顏色相組合,就可以得到我們所需要的各種顏色,而alpha不直接影響顏色,它將留待以后介紹。

            RGBA模式下選擇顏色是十分簡(jiǎn)單的事情,只需要一個(gè)函數(shù)就可以搞定。

            glColor*系列函數(shù)可以用于設(shè)置顏色,其中三個(gè)參數(shù)的版本可以指定RGB的值,而A值采用默認(rèn);四個(gè)參數(shù)的版本可以分別指定RGBA的值。例如:

            void glColor3f(GLfloat red, GLfloat green, GLfloat blue);

            void glColor4f(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);

            (還記得嗎?3f表示有三個(gè)浮點(diǎn)參數(shù)~請(qǐng)看第二課中關(guān)于glVertex*函數(shù)的敘述。)

            將浮點(diǎn)數(shù)作為參數(shù),其中0.0表示不使用該種顏色,而1.0表示將該種顏色用到最多。例如:

            glColor3f(1.0f, 0.0f, 0.0f);   表示不使用綠、藍(lán)色,而將紅色使用最多,于是得到最純凈的紅色。

            glColor3f(0.0f, 1.0f, 1.0f);   表示使用綠、藍(lán)色到最多,而不使用紅色。混合的效果就是淺藍(lán)色。

            glColor3f(0.5f, 0.5f, 0.5f);   表示各種顏色使用一半,效果為灰色。

            注意:浮點(diǎn)數(shù)可以精確到小數(shù)點(diǎn)后若干位,這并不表示計(jì)算機(jī)就可以顯示如此多種顏色。實(shí)際上,計(jì)算機(jī)可以顯示的顏色種數(shù)將由硬件決定。如果OpenGL找不到精確的顏色,會(huì)進(jìn)行類似四舍五入的處理。

             

            大家可以通過(guò)改變下面代碼中glColor3f的參數(shù)值,繪制不同顏色的矩形。

            void myDisplay(void)

            {

                glClear(GL_COLOR_BUFFER_BIT);

                glColor3f(0.0f, 1.0f, 1.0f);

                glRectf(-0.5f, -0.5f, 0.5f, 0.5f);

                glFlush();

            }

             

            注意:glColor系列函數(shù),在參數(shù)類型不同時(shí),表示最大顏色的值也不同。

            采用fd做后綴的函數(shù),以1.0表示最大的使用。

            采用b做后綴的函數(shù),以127表示最大的使用。

            采用ub做后綴的函數(shù),以255表示最大的使用。

            采用s做后綴的函數(shù),以32767表示最大的使用。

            采用us做后綴的函數(shù),以65535表示最大的使用。

            這些規(guī)則看似麻煩,但熟悉后實(shí)際使用中不會(huì)有什么障礙。

             

            2、索引顏色

            在索引顏色模式中,OpenGL需要一個(gè)顏色表。這個(gè)表就相當(dāng)于畫(huà)家的調(diào)色板:雖然可以調(diào)出很多種顏色,但同時(shí)存在于調(diào)色板上的顏色種數(shù)將不會(huì)超過(guò)調(diào)色板的格數(shù)。試將顏色表的每一項(xiàng)想象成調(diào)色板上的一個(gè)格子:它保存了一種顏色。

            在使用索引顏色模式畫(huà)圖時(shí),我說(shuō)我把第i種顏色設(shè)置為某某,其實(shí)就相當(dāng)于將調(diào)色板的第i格調(diào)為某某顏色。我需要第k種顏色來(lái)畫(huà)圖,那么就用畫(huà)筆去蘸一下第k格調(diào)色板。

            顏色表的大小是很有限的,一般在256~4096之間,且總是2的整數(shù)次冪。在使用索引顏色方式進(jìn)行繪圖時(shí),總是先設(shè)置顏色表,然后選擇顏色。

             

            2.1、選擇顏色

            使用glIndex*系列函數(shù)可以在顏色表中選擇顏色。其中最常用的可能是glIndexi,它的參數(shù)是一個(gè)整形。

            void glIndexi(GLint c);

            是的,這的確很簡(jiǎn)單。

             

            2.2、設(shè)置顏色表

            OpenGL 并直接沒(méi)有提供設(shè)置顏色表的方法,因此設(shè)置顏色表需要使用操作系統(tǒng)的支持。我們所用的Windows和其他大多數(shù)圖形操作系統(tǒng)都具有這個(gè)功能,但所使用的函數(shù)卻不相同。正如我沒(méi)有講述如何自己寫(xiě)代碼在Windows下建立一個(gè)窗口,這里我也不會(huì)講述如何在Windows下設(shè)置顏色表。

            GLUT工具包提供了設(shè)置顏色表的函數(shù)glutSetColor,但我測(cè)試始終有問(wèn)題。現(xiàn)在為了讓大家體驗(yàn)一下索引顏色,我向大家介紹另一個(gè)OpenGL工具包: aux。這個(gè)工具包是VisualStudio自帶的,不必另外安裝,但它已經(jīng)過(guò)時(shí),這里僅僅是體驗(yàn)一下,大家不必深入。

            #include <windows.h>

            #include <GL/gl.h>

            #include <GL/glaux.h>

             

            #pragma comment (lib, "opengl32.lib")

            #pragma comment (lib, "glaux.lib")

             

            #include <math.h>

            const GLdouble Pi = 3.1415926536;

            void myDisplay(void)

            {

                int i;

                for(i=0; i<8; ++i)

                    auxSetOneColor(i, (float)(i&0x04), (float)(i&0x02), (float)(i&0x01));

                glShadeModel(GL_FLAT);

                glClear(GL_COLOR_BUFFER_BIT);

                glBegin(GL_TRIANGLE_FAN);

                glVertex2f(0.0f, 0.0f);

                for(i=0; i<=8; ++i)

                {

                    glIndexi(i);

                    glVertex2f(cos(i*Pi/4), sin(i*Pi/4));

                }

                glEnd();

                glFlush();

            }

             

            int main(void)

            {

                auxInitDisplayMode(AUX_SINGLE|AUX_INDEX);

                auxInitPosition(0, 0, 400, 400);

                auxInitWindow(L"");

                myDisplay();

                Sleep(10 * 1000);

                return 0;

            }

             

            其它部分大家都可以不管,只看myDisplay函數(shù)就可以了。首先,使用auxSetOneColor設(shè)置顏色表中的一格。循環(huán)八次就可以設(shè)置八格。

            glShadeModel等下再講,這里不提。

            然后在循環(huán)中用glVertex設(shè)置頂點(diǎn),同時(shí)用glIndexi改變頂點(diǎn)代表的顏色。

            最終得到的效果是八個(gè)相同形狀、不同顏色的三角形。

             

            索引顏色雖然講得多了點(diǎn)。索引顏色的主要優(yōu)勢(shì)是占用空間小(每個(gè)像素不必單獨(dú)保存自己的顏色,只用很少的二進(jìn)制位就可以代表其顏色在顏色表中的位置),花費(fèi)系統(tǒng)資源少,圖形運(yùn)算速度快,但它編程稍稍顯得不是那么方便,并且畫(huà)面效果也會(huì)比RGB顏色差一些。星際爭(zhēng)霸可能代表了256色的顏色表的畫(huà)面效果,雖然它在一臺(tái)很爛的PC上也可以運(yùn)行很流暢,但以目前的眼光來(lái)看,其畫(huà)面效果就顯得不足了。

            目前的PC機(jī)性能已經(jīng)足夠在各種場(chǎng)合下使用RGB顏色,因此PC程序開(kāi)發(fā)中,使用索引顏色已經(jīng)不是主流。當(dāng)然,一些小型設(shè)備例如GBA、手機(jī)等,索引顏色還是有它的用武之地。

             

             

            3、指定清除屏幕用的顏色

            我們寫(xiě):glClear(GL_COLOR_BUFFER_BIT);意思是把屏幕上的顏色清空。

            但實(shí)際上什么才叫呢?在宇宙中,黑色代表了;在一張白紙上,白色代表了;在信封上,信封的顏色才是

            OpenGL用下面的函數(shù)來(lái)定義清楚屏幕后屏幕所擁有的顏色。

            RGB模式下,使用glClearColor來(lái)指定的顏色,它需要四個(gè)參數(shù),其參數(shù)的意義跟glColor4f相似。

            在索引顏色模式下,使用glClearIndex來(lái)指定的顏色所在的索引,它需要一個(gè)參數(shù),其意義跟glIndexi相似。

            void myDisplay(void)

            {

                glClearColor(1.0f, 0.0f, 0.0f, 0.0f);

                glClear(GL_COLOR_BUFFER_BIT);

                glFlush();

            }

            呵,這個(gè)還真簡(jiǎn)單~

             

             

            4、指定著色模型

            OpenGL允許為同一多邊形的不同頂點(diǎn)指定不同的顏色。例如:

            #include <math.h>

            const GLdouble Pi = 3.1415926536;

            void myDisplay(void)

            {

                int i;

                // glShadeModel(GL_FLAT);

                glClear(GL_COLOR_BUFFER_BIT);

                glBegin(GL_TRIANGLE_FAN);

                glColor3f(1.0f, 1.0f, 1.0f);

                glVertex2f(0.0f, 0.0f);

                for(i=0; i<=8; ++i)

                {

                    glColor3f(i&0x04, i&0x02, i&0x01);

                    glVertex2f(cos(i*Pi/4), sin(i*Pi/4));

                }

                glEnd();

                glFlush();

            }

            在默認(rèn)情況下,OpenGL會(huì)計(jì)算兩點(diǎn)頂點(diǎn)之間的其它點(diǎn),并為它們填上合適的顏色,使相鄰的點(diǎn)的顏色值都比較接近。如果使用的是RGB模式,看起來(lái)就具有漸變的效果。如果是使用顏色索引模式,則其相鄰點(diǎn)的索引值是接近的,如果將顏色表中接近的項(xiàng)設(shè)置成接近的顏色,則看起來(lái)也是漸變的效果。但如果顏色表中接近的項(xiàng)顏色卻差距很大,則看起來(lái)可能是很奇怪的效果。

            使用glShadeModel函數(shù)可以關(guān)閉這種計(jì)算,如果頂點(diǎn)的顏色不同,則將頂點(diǎn)之間的其它點(diǎn)全部設(shè)置為與某一個(gè)點(diǎn)相同。(直線以后指定的點(diǎn)的顏色為準(zhǔn),而多邊形將以任意頂點(diǎn)的顏色為準(zhǔn),由實(shí)現(xiàn)決定。)為了避免這個(gè)不確定性,盡量在多邊形中使用同一種顏色。

            glShadeModel的使用方法:

            glShadeModel(GL_SMOOTH);   // 平滑方式,這也是默認(rèn)方式

            glShadeModel(GL_FLAT);     // 單色方式

             

            小結(jié):

            本課學(xué)習(xí)了如何設(shè)置顏色。其中RGB顏色方式是目前PC機(jī)上的常用方式。

            可以設(shè)置glClear清除后屏幕所剩的顏色。

            可以設(shè)置顏色填充方式:平滑方式或單色方式。

             

            =====================   第四課    =====================

            OpenGL入門(mén)學(xué)習(xí)(五)(轉(zhuǎn))

            今天要講的是三維變換的內(nèi)容,課程比較枯燥。主要是因?yàn)楹芏嗪瘮?shù)在單獨(dú)使用時(shí)都不好描述其效果,我只好在最后舉一個(gè)比較綜合的例子。希望大家能一口氣看到底了。只看一次可能不夠,如果感覺(jué)到迷糊,不妨多看兩遍。有疑問(wèn)可以在下面跟帖提出。

            我也使用了若干圖形,希望可以幫助理解。

             

             

            在前面繪制幾何圖形的時(shí)候,大家是否覺(jué)得我們繪圖的范圍太狹隘了呢?坐標(biāo)只能從-11,還只能是X軸向右,Y軸向上,Z軸垂直屏幕。這些限制給我們的繪圖帶來(lái)了很多不便。

             

            我們生活在一個(gè)三維的世界——如果要觀察一個(gè)物體,我們可以:

            1、從不同的位置去觀察它。(視圖變換)

            2、移動(dòng)或者旋轉(zhuǎn)它,當(dāng)然了,如果它只是計(jì)算機(jī)里面的物體,我們還可以放大或縮小它。(模型變換)

            3、如果把物體畫(huà)下來(lái),我們可以選擇:是否需要一種近大遠(yuǎn)小的透視效果。另外,我們可能只希望看到物體的一部分,而不是全部(剪裁)。(投影變換)

            4、我們可能希望把整個(gè)看到的圖形畫(huà)下來(lái),但它只占據(jù)紙張的一部分,而不是全部。(視口變換)

            這些,都可以在OpenGL中實(shí)現(xiàn)。

             

            OpenGL變換實(shí)際上是通過(guò)矩陣乘法來(lái)實(shí)現(xiàn)。無(wú)論是移動(dòng)、旋轉(zhuǎn)還是縮放大小,都是通過(guò)在當(dāng)前矩陣的基礎(chǔ)上乘以一個(gè)新的矩陣來(lái)達(dá)到目的。關(guān)于矩陣的知識(shí),這里不詳細(xì)介紹,有興趣的朋友可以看看線性代數(shù)(大學(xué)生的話多半應(yīng)該學(xué)過(guò)的)。

            OpenGL可以在最底層直接操作矩陣,不過(guò)作為初學(xué),這樣做的意義并不大。這里就不做介紹了。

             

             

            1、模型變換和視圖變換

            相對(duì)移動(dòng)的觀點(diǎn)來(lái)看,改變觀察點(diǎn)的位置與方向和改變物體本身的位置與方向具有等效性。在OpenGL中,實(shí)現(xiàn)這兩種功能甚至使用的是同樣的函數(shù)。

            由于模型和視圖的變換都通過(guò)矩陣運(yùn)算來(lái)實(shí)現(xiàn),在進(jìn)行變換前,應(yīng)先設(shè)置當(dāng)前操作的矩陣為模型視圖矩陣。設(shè)置的方法是以GL_MODELVIEW為參數(shù)調(diào)用glMatrixMode函數(shù),像這樣:

            glMatrixMode(GL_MODELVIEW);

            通常,我們需要在進(jìn)行變換前把當(dāng)前矩陣設(shè)置為單位矩陣。這也只需要一行代碼:

            glLoadIdentity();

             

            然后,就可以進(jìn)行模型變換和視圖變換了。進(jìn)行模型和視圖變換,主要涉及到三個(gè)函數(shù):

            glTranslate*,把當(dāng)前矩陣和一個(gè)表示移動(dòng)物體的矩陣相乘。三個(gè)參數(shù)分別表示了在三個(gè)坐標(biāo)上的位移值。

            glRotate*,把當(dāng)前矩陣和一個(gè)表示旋轉(zhuǎn)物體的矩陣相乘。物體將繞著(0,0,0)(x,y,z)的直線以逆時(shí)針旋轉(zhuǎn),參數(shù)angle表示旋轉(zhuǎn)的角度。

            glScale*,把當(dāng)前矩陣和一個(gè)表示縮放物體的矩陣相乘。x,y,z分別表示在該方向上的縮放比例。

             

            注意我都是說(shuō)XX相乘,而不是直接說(shuō)這個(gè)函數(shù)就是旋轉(zhuǎn)或者這個(gè)函數(shù)就是移動(dòng),這是有原因的,馬上就會(huì)講到。

            假設(shè)當(dāng)前矩陣為單位矩陣,然后先乘以一個(gè)表示旋轉(zhuǎn)的矩陣R,再乘以一個(gè)表示移動(dòng)的矩陣T,最后得到的矩陣再乘上每一個(gè)頂點(diǎn)的坐標(biāo)矩陣v。所以,經(jīng)過(guò)變換得到的頂點(diǎn)坐標(biāo)就是((RT)v)。由于矩陣乘法的結(jié)合率,((RT)v) = (R(Tv)),換句話說(shuō),實(shí)際上是先進(jìn)行移動(dòng),然后進(jìn)行旋轉(zhuǎn)。即:實(shí)際變換的順序與代碼中寫(xiě)的順序是相反的。由于先移動(dòng)后旋轉(zhuǎn)先旋轉(zhuǎn)后移動(dòng)得到的結(jié)果很可能不同,初學(xué)的時(shí)候需要特別注意這一點(diǎn)。

            OpenGL之所以這樣設(shè)計(jì),是為了得到更高的效率。但在繪制復(fù)雜的三維圖形時(shí),如果每次都去考慮如何把變換倒過(guò)來(lái),也是很痛苦的事情。這里介紹另一種思路,可以讓代碼看起來(lái)更自然(寫(xiě)出的代碼其實(shí)完全一樣,只是考慮問(wèn)題時(shí)用的方法不同了)。

            讓我們想象,坐標(biāo)并不是固定不變的。旋轉(zhuǎn)的時(shí)候,坐標(biāo)系統(tǒng)隨著物體旋轉(zhuǎn)。移動(dòng)的時(shí)候,坐標(biāo)系統(tǒng)隨著物體移動(dòng)。如此一來(lái),就不需要考慮代碼的順序反轉(zhuǎn)的問(wèn)題了。

             

            以上都是針對(duì)改變物體的位置和方向來(lái)介紹的。如果要改變觀察點(diǎn)的位置,除了配合使用glRotate*glTranslate*函數(shù)以外,還可以使用這個(gè)函數(shù):gluLookAt。它的參數(shù)比較多,前三個(gè)參數(shù)表示了觀察點(diǎn)的位置,中間三個(gè)參數(shù)表示了觀察目標(biāo)的位置,最后三個(gè)參數(shù)代表從(0,0,0) (x,y,z)的直線,它表示了觀察者認(rèn)為的方向。

             

             

            2、投影變換

            投影變換就是定義一個(gè)可視空間,可視空間以外的物體不會(huì)被繪制到屏幕上。(注意,從現(xiàn)在起,坐標(biāo)可以不再是-1.01.0了!)

            OpenGL支持兩種類型的投影變換,即透視投影和正投影。投影也是使用矩陣來(lái)實(shí)現(xiàn)的。如果需要操作投影矩陣,需要以GL_PROJECTION為參數(shù)調(diào)用glMatrixMode函數(shù)。

            glMatrixMode(GL_PROJECTION);

            通常,我們需要在進(jìn)行變換前把當(dāng)前矩陣設(shè)置為單位矩陣。

            glLoadIdentity();

             

            透視投影所產(chǎn)生的結(jié)果類似于照片,有近大遠(yuǎn)小的效果,比如在火車頭內(nèi)向前照一個(gè)鐵軌的照片,兩條鐵軌似乎在遠(yuǎn)處相交了。

            使用glFrustum函數(shù)可以將當(dāng)前的可視空間設(shè)置為透視投影空間。其參數(shù)的意義如下圖:

            聲明:該圖片來(lái)自www.opengl.org,該圖片是《OpenGL編程指南》一書(shū)的附圖,由于該書(shū)的舊版(第一版,1994年)已經(jīng)流傳于網(wǎng)絡(luò),我希望沒(méi)有觸及到版權(quán)問(wèn)題。

            也可以使用更常用的gluPerspective函數(shù)。其參數(shù)的意義如下圖:

            聲明:該圖片來(lái)自www.opengl.org,該圖片是《OpenGL編程指南》一書(shū)的附圖,由于該書(shū)的舊版(第一版,1994年)已經(jīng)流傳于網(wǎng)絡(luò),我希望沒(méi)有觸及到版權(quán)問(wèn)題。

             

            正投影相當(dāng)于在無(wú)限遠(yuǎn)處觀察得到的結(jié)果,它只是一種理想狀態(tài)。但對(duì)于計(jì)算機(jī)來(lái)說(shuō),使用正投影有可能獲得更好的運(yùn)行速度。

            使用glOrtho函數(shù)可以將當(dāng)前的可視空間設(shè)置為正投影空間。其參數(shù)的意義如下圖:

            聲明:該圖片來(lái)自www.opengl.org,該圖片是《OpenGL編程指南》一書(shū)的附圖,由于該書(shū)的舊版(第一版,1994年)已經(jīng)流傳于網(wǎng)絡(luò),我希望沒(méi)有觸及到版權(quán)問(wèn)題。

             

            如果繪制的圖形空間本身就是二維的,可以使用gluOrtho2D。他的使用類似于glOrgho

             

             

            3、視口變換

            當(dāng)一切工作已經(jīng)就緒,只需要把像素繪制到屏幕上了。這時(shí)候還剩最后一個(gè)問(wèn)題:應(yīng)該把像素繪制到窗口的哪個(gè)區(qū)域呢?通常情況下,默認(rèn)是完整的填充整個(gè)窗口,但我們完全可以只填充一半。(即:把整個(gè)圖象填充到一半的窗口內(nèi))

            聲明:該圖片來(lái)自www.opengl.org,該圖片是《OpenGL編程指南》一書(shū)的附圖,由于該書(shū)的舊版(第一版,1994年)已經(jīng)流傳于網(wǎng)絡(luò),我希望沒(méi)有觸及到版權(quán)問(wèn)題。

             

            使用glViewport來(lái)定義視口。其中前兩個(gè)參數(shù)定義了視口的左下腳(0,0表示最左下方),后兩個(gè)參數(shù)分別是寬度和高度。

             

            4、操作矩陣堆棧

            介于是入門(mén)教程,先簡(jiǎn)單介紹一下堆棧。你可以把堆棧想象成一疊盤(pán)子。開(kāi)始的時(shí)候一個(gè)盤(pán)子也沒(méi)有,你可以一個(gè)一個(gè)往上放,也可以一個(gè)一個(gè)取下來(lái)。每次取下的,都是最后一次被放上去的盤(pán)子。通常,在計(jì)算機(jī)實(shí)現(xiàn)堆棧時(shí),堆棧的容量是有限的,如果盤(pán)子過(guò)多,就會(huì)出錯(cuò)。當(dāng)然,如果沒(méi)有盤(pán)子了,再要求取一個(gè)盤(pán)子,也會(huì)出錯(cuò)。

            我們?cè)谶M(jìn)行矩陣操作時(shí),有可能需要先保存某個(gè)矩陣,過(guò)一段時(shí)間再恢復(fù)它。當(dāng)我們需要保存時(shí),調(diào)用glPushMatrix函數(shù),它相當(dāng)于把矩陣(相當(dāng)于盤(pán)子)放到堆棧上。當(dāng)需要恢復(fù)最近一次的保存時(shí),調(diào)用glPopMatrix函數(shù),它相當(dāng)于把矩陣從堆棧上取下。OpenGL規(guī)定堆棧的容量至少可以容納32個(gè)矩陣,某些OpenGL實(shí)現(xiàn)中,堆棧的容量實(shí)際上超過(guò)了32個(gè)。因此不必過(guò)于擔(dān)心矩陣的容量問(wèn)題。

            通常,用這種先保存后恢復(fù)的措施,比先變換再逆變換要更方便,更快速。

            注意:模型視圖矩陣和投影矩陣都有相應(yīng)的堆棧。使用glMatrixMode來(lái)指定當(dāng)前操作的究竟是模型視圖矩陣還是投影矩陣。

             

            5、綜合舉例

            好了,視圖變換的入門(mén)知識(shí)差不多就講完了。但我們不能就這樣結(jié)束。因?yàn)楸敬握n程的內(nèi)容實(shí)在過(guò)于枯燥,如果分別舉例,可能效果不佳。我只好綜合的講一個(gè)例子,算是給大家一個(gè)參考。至于實(shí)際的掌握,還要靠大家自己花功夫。閑話少說(shuō),現(xiàn)在進(jìn)入正題。

             

            我們要制作的是一個(gè)三維場(chǎng)景,包括了太陽(yáng)、地球和月亮。假定一年有12個(gè)月,每個(gè)月30天。每年,地球繞著太陽(yáng)轉(zhuǎn)一圈。每個(gè)月,月亮圍著地球轉(zhuǎn)一圈。即一年有360天。現(xiàn)在給出日期的編號(hào)(0~359),要求繪制出太陽(yáng)、地球、月亮的相對(duì)位置示意圖。(這是為了編程方便才這樣設(shè)計(jì)的。如果需要制作更現(xiàn)實(shí)的情況,那也只是一些數(shù)值處理而已,與OpenGL關(guān)系不大)

            首先,讓我們認(rèn)定這三個(gè)天體都是球形,且他們的運(yùn)動(dòng)軌跡處于同一水平面,建立以下坐標(biāo)系:太陽(yáng)的中心為原點(diǎn),天體軌跡所在的平面表示了X軸與Y軸決定的平面,且每年第一天,地球在X軸正方向上,月亮在地球的正X軸方向。

            下一步是確立可視空間。注意:太陽(yáng)的半徑要比太陽(yáng)到地球的距離短得多。如果我們直接使用天文觀測(cè)得到的長(zhǎng)度比例,則當(dāng)整個(gè)窗口表示地球軌道大小時(shí),太陽(yáng)的大小將被忽略。因此,我們只能成倍的放大幾個(gè)天體的半徑,以適應(yīng)我們觀察的需要。(百度一下,得到太陽(yáng)、地球、月亮的大致半徑分別是:696000km  6378km1738km。地球到太陽(yáng)的距離約為1.5km=150000000km,月亮到地球的距離約為380000km。)

            讓我們假想一些數(shù)據(jù),將三個(gè)天體的半徑分別修改為:69600000(放大100倍),15945000(放大2500倍),4345000(放大5000倍)。將地球到月亮的距離修改38000000(放大100倍)。地球到太陽(yáng)的距離保持不變。

            為了讓地球和月亮在離我們很近時(shí),我們?nèi)匀徊恍枰儞Q觀察點(diǎn)和觀察方向就可以觀察它們,我們把觀察點(diǎn)放在這個(gè)位置:(0, -200000000, 0)  ——因?yàn)榈厍蜍壍腊霃綖?/span>150000000,咱們就湊個(gè)整,取-200000000就可以了。觀察目標(biāo)設(shè)置為原點(diǎn)(即太陽(yáng)中心),選擇Z軸正方向作為  “方。當(dāng)然我們還可以把觀察點(diǎn)往方移動(dòng)一些,得到(0, -200000000, 200000000),這樣可以得到45度角的俯視效果。

            為了得到透視效果,我們使用gluPerspective來(lái)設(shè)置可視空間。假定可視角為60度(如果調(diào)試時(shí)發(fā)現(xiàn)該角度不合適,可修改之。我在最后選擇的數(shù)值是75。),高寬比為1.0。最近可視距離為1.0,最遠(yuǎn)可視距離為200000000*2=400000000。即:gluPerspective  (60, 1, 1, 400000000);

             

             

            5、綜合舉例

            好了,視圖變換的入門(mén)知識(shí)差不多就講完了。但我們不能就這樣結(jié)束。因?yàn)楸敬握n程的內(nèi)容實(shí)在過(guò)于枯燥,如果分別舉例,可能效果不佳。我只好綜合的講一個(gè)例子,算是給大家一個(gè)參考。至于實(shí)際的掌握,還要靠大家自己花功夫。閑話少說(shuō),現(xiàn)在進(jìn)入正題。

             

            我們要制作的是一個(gè)三維場(chǎng)景,包括了太陽(yáng)、地球和月亮。假定一年有12個(gè)月,每個(gè)月30天。每年,地球繞著太陽(yáng)轉(zhuǎn)一圈。每個(gè)月,月亮圍著地球轉(zhuǎn)一圈。即一年有360天。現(xiàn)在給出日期的編號(hào)(0~359),要求繪制出太陽(yáng)、地球、月亮的相對(duì)位置示意圖。(這是為了編程方便才這樣設(shè)計(jì)的。如果需要制作更現(xiàn)實(shí)的情況,那也只是一些數(shù)值處理而已,與OpenGL關(guān)系不大)

            首先,讓我們認(rèn)定這三個(gè)天體都是球形,且他們的運(yùn)動(dòng)軌跡處于同一水平面,建立以下坐標(biāo)系:太陽(yáng)的中心為原點(diǎn),天體軌跡所在的平面表示了X軸與Y軸決定的平面,且每年第一天,地球在X軸正方向上,月亮在地球的正X軸方向。

            下一步是確立可視空間。注意:太陽(yáng)的半徑要比太陽(yáng)到地球的距離短得多。如果我們直接使用天文觀測(cè)得到的長(zhǎng)度比例,則當(dāng)整個(gè)窗口表示地球軌道大小時(shí),太陽(yáng)的大小將被忽略。因此,我們只能成倍的放大幾個(gè)天體的半徑,以適應(yīng)我們觀察的需要。(百度一下,得到太陽(yáng)、地球、月亮的大致半徑分別是:696000km 6378km1738km。地球到太陽(yáng)的距離約為1.5km=150000000km,月亮到地球的距離約為380000km。)

            讓我們假想一些數(shù)據(jù),將三個(gè)天體的半徑分別修改為:69600000(放大100倍),15945000(放大2500倍),4345000(放大2500倍)。將地球到月亮的距離修改38000000(放大100倍)。地球到太陽(yáng)的距離保持不變。

            為了讓地球和月亮在離我們很近時(shí),我們?nèi)匀徊恍枰儞Q觀察點(diǎn)和觀察方向就可以觀察它們,我們把觀察點(diǎn)放在這個(gè)位置:(0, -200000000, 0) ——因?yàn)榈厍蜍壍腊霃綖?/span>150000000,咱們就湊個(gè)整,取-200000000就可以了。觀察目標(biāo)設(shè)置為原點(diǎn)(即太陽(yáng)中心),選擇Z軸正方向作為 方。當(dāng)然我們還可以把觀察點(diǎn)往方移動(dòng)一些,得到(0, -200000000, 200000000),這樣可以得到45度角的俯視效果。

            為了得到透視效果,我們使用gluPerspective來(lái)設(shè)置可視空間。假定可視角為60度(如果調(diào)試時(shí)發(fā)現(xiàn)該角度不合適,可修改之。我在最后選擇的數(shù)值是75。),高寬比為1.0。最近可視距離為1.0,最遠(yuǎn)可視距離為200000000*2=400000000。即:gluPerspective (60, 1, 1, 400000000);

             

             

            現(xiàn)在我們來(lái)看看如何繪制這三個(gè)天體。

            為了簡(jiǎn)單起見(jiàn),我們把三個(gè)天體都想象成規(guī)則的球體。而我們所使用的glut實(shí)用工具中,正好就有一個(gè)繪制球體的現(xiàn)成函數(shù):glutSolidSphere,這個(gè)函數(shù)在原點(diǎn)繪制出一個(gè)球體。由于坐標(biāo)是可以通過(guò)glTranslate*glRotate*兩個(gè)函數(shù)進(jìn)行隨意變換的,所以我們就可以在任意位置繪制球體了。函數(shù)有三個(gè)參數(shù):第一個(gè)參數(shù)表示球體的半徑,后兩個(gè)參數(shù)代表了的數(shù)目,簡(jiǎn)單點(diǎn)說(shuō)就是球體的精確程度,數(shù)值越大越精確,當(dāng)然代價(jià)就是速度越緩慢。這里我們只是簡(jiǎn)單的設(shè)置后兩個(gè)參數(shù)為20

            太陽(yáng)在坐標(biāo)原點(diǎn),所以不需要經(jīng)過(guò)任何變換,直接繪制就可以了。

            地球則要復(fù)雜一點(diǎn),需要變換坐標(biāo)。由于今年已經(jīng)經(jīng)過(guò)的天數(shù)已知為day,則地球轉(zhuǎn)過(guò)的角度為day/一年的天數(shù)*360度。前面已經(jīng)假定每年都是360天,因此地球轉(zhuǎn)過(guò)的角度恰好為day。所以可以通過(guò)下面的代碼來(lái)解決:

            glRotatef(day, 0, 0, -1);

            /* 注意地球公轉(zhuǎn)是自西向東的,因此是饒著Z軸負(fù)方向進(jìn)行逆時(shí)針旋轉(zhuǎn) */

            glTranslatef(地球軌道半徑, 0, 0);

            glutSolidSphere(地球半徑, 20, 20);

            月亮是最復(fù)雜的。因?yàn)樗粌H要繞地球轉(zhuǎn),還要隨著地球繞太陽(yáng)轉(zhuǎn)。但如果我們選擇地球作為參考,則月亮進(jìn)行的運(yùn)動(dòng)就是一個(gè)簡(jiǎn)單的圓周運(yùn)動(dòng)了。如果我們先繪制地球,再繪制月亮,則只需要進(jìn)行與地球類似的變換:

            glRotatef(月亮旋轉(zhuǎn)的角度, 0, 0, -1);

            glTranslatef(月亮軌道半徑, 0, 0);

            glutSolidSphere(月亮半徑, 20, 20);

            但這個(gè)月亮旋轉(zhuǎn)的角度,并不能簡(jiǎn)單的理解為day/一個(gè)月的天數(shù)30*360度。因?yàn)槲覀冊(cè)诶L制地球時(shí),這個(gè)坐標(biāo)已經(jīng)是旋轉(zhuǎn)過(guò)的。現(xiàn)在的旋轉(zhuǎn)是在以前的基礎(chǔ)上進(jìn)行旋轉(zhuǎn),因此還需要處理這個(gè)差值。我們可以寫(xiě)成:day/30*360 - day,即減去原來(lái)已經(jīng)轉(zhuǎn)過(guò)的角度。這只是一種簡(jiǎn)單的處理,當(dāng)然也可以在繪制地球前用glPushMatrix保存矩陣,繪制地球后用glPopMatrix恢復(fù)矩陣。再設(shè)計(jì)一個(gè)跟地球位置無(wú)關(guān)的月亮位置公式,來(lái)繪制月亮。通常后一種方法比前一種要好,因?yàn)楦↑c(diǎn)的運(yùn)算是不精確的,即是說(shuō)我們計(jì)算地球本身的位置就是不精確的。拿這個(gè)不精確的數(shù)去計(jì)算月亮的位置,會(huì)導(dǎo)致 不精確的成分累積,過(guò)多的不精確會(huì)造成錯(cuò)誤。我們這個(gè)小程序沒(méi)有去考慮這個(gè),但并不是說(shuō)這個(gè)問(wèn)題不重要。

            還有一個(gè)需要注意的細(xì)節(jié): OpenGL把三維坐標(biāo)中的物體繪制到二維屏幕,繪制的順序是按照代碼的順序來(lái)進(jìn)行的。因此后繪制的物體會(huì)遮住先繪制的物體,即使后繪制的物體在先繪制的物體的后面也是如此。使用深度測(cè)試可以解決這一問(wèn)題。使用的方法是:1、以GL_DEPTH_TEST為參數(shù)調(diào)用glEnable函數(shù),啟動(dòng)深度測(cè)試。2、在必要時(shí)(通常是每次繪制畫(huà)面開(kāi)始時(shí)),清空深度緩沖,即:glClear(GL_DEPTH_BUFFER_BIT);其中,glClear (GL_COLOR_BUFFER_BIT)glClear(GL_DEPTH_BUFFER_BIT)可以合并寫(xiě)為:

            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

            且后者的運(yùn)行速度可能比前者快。

             

             

            到此為止,我們終于可以得到整個(gè)太陽(yáng),地球和月亮系統(tǒng)的完整代碼。

             

             

            Code:

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

            // 太陽(yáng)、地球和月亮

            // 假設(shè)每個(gè)月都是30

            // 一年12個(gè)月,共是360

            static int day = 200; // day的變化:從0359

            void myDisplay(void)

            {

                glEnable(GL_DEPTH_TEST);

                glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

             

                glMatrixMode(GL_PROJECTION);

                glLoadIdentity();

                gluPerspective(75, 1, 1, 400000000);

                glMatrixMode(GL_MODELVIEW);

                glLoadIdentity();

                gluLookAt(0, -200000000, 200000000, 0, 0, 0, 0, 0, 1);

             

                // 繪制紅色的太陽(yáng)

                glColor3f(1.0f, 0.0f, 0.0f);

                glutSolidSphere(69600000, 20, 20);

                // 繪制藍(lán)色的地球

                glColor3f(0.0f, 0.0f, 1.0f);

                glRotatef(day/360.0*360.0, 0.0f, 0.0f, -1.0f);

                glTranslatef(150000000, 0.0f, 0.0f);

                glutSolidSphere(15945000, 20, 20);

                // 繪制黃色的月亮

                glColor3f(1.0f, 1.0f, 0.0f);

                glRotatef(day/30.0*360.0 - day/360.0*360.0, 0.0f, 0.0f, -1.0f);

                glTranslatef(38000000, 0.0f, 0.0f);

                glutSolidSphere(4345000, 20, 20);

             

                glFlush();

            }

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

             

             

             

            試修改day的值,看看畫(huà)面有何變化。

             

             

            小結(jié):本課開(kāi)始,我們正式進(jìn)入了三維的OpenGL世界。

            OpenGL通過(guò)矩陣變換來(lái)把三維物體轉(zhuǎn)變?yōu)槎S圖象,進(jìn)而在屏幕上顯示出來(lái)。為了指定當(dāng)前操作的是何種矩陣,我們使用了函數(shù)glMatrixMode

            我們可以移動(dòng)、旋轉(zhuǎn)觀察點(diǎn)或者移動(dòng)、旋轉(zhuǎn)物體,使用的函數(shù)是glTranslate*glRotate*

            我們可以縮放物體,使用的函數(shù)是glScale*

            我們可以定義可視空間,這個(gè)空間可以是正投影的(使用glOrthogluOrtho2D),也可以是透視投影的(使用glFrustumgluPerspective)。

            我們可以定義繪制到窗口的范圍,使用的函數(shù)是glViewport

            矩陣有自己的堆棧,方便進(jìn)行保存和恢復(fù)。這在繪制復(fù)雜圖形時(shí)很有幫助。使用的函數(shù)是glPushMatrixglPopMatrix

             

            好了,艱苦的一課終于完畢。我知道,本課的內(nèi)容十分枯燥,就連最后的例子也是。但我也沒(méi)有更好的辦法了,希望大家能堅(jiān)持過(guò)去。不必?fù)?dān)心,熟悉本課內(nèi)容后,以后的一段時(shí)間內(nèi),都會(huì)是比較輕松愉快的了。

             

            =====================   第五課    =====================

            OpenGL入門(mén)學(xué)習(xí)(六)(轉(zhuǎn))

            今天要講的是動(dòng)畫(huà)制作——可能是各位都很喜歡的。除了講授知識(shí)外,我們還會(huì)讓昨天那個(gè)太陽(yáng)、地球和月亮天體圖畫(huà)動(dòng)起來(lái)。緩和一下枯燥的氣氛。本次課程,我們將進(jìn)入激動(dòng)人心的計(jì)算機(jī)動(dòng)畫(huà)世界。

             

            想必大家都知道電影和動(dòng)畫(huà)的工作原理吧?是的,快速的把看似連續(xù)的畫(huà)面一幅幅的呈現(xiàn)在人們面前。一旦每秒鐘呈現(xiàn)的畫(huà)面超過(guò)24幅,人們就會(huì)錯(cuò)以為它是連續(xù)的。我們通常觀看的電視,每秒播放2530幅畫(huà)面。但對(duì)于計(jì)算機(jī)來(lái)說(shuō),它可以播放更多的畫(huà)面,以達(dá)到更平滑的效果。如果速度過(guò)慢,畫(huà)面不夠平滑。如果速度過(guò)快,則人眼未必就能反應(yīng)得過(guò)來(lái)。對(duì)于一個(gè)正常人來(lái)說(shuō),每秒60~120幅圖畫(huà)是比較合適的。具體的數(shù)值因人而異。

             

            假設(shè)某動(dòng)畫(huà)一共有n幅畫(huà)面,則它的工作步驟就是:

            顯示第1幅畫(huà)面,然后等待一小段時(shí)間,直到下一個(gè)1/24

            顯示第2幅畫(huà)面,然后等待一小段時(shí)間,直到下一個(gè)1/24

            ……

            顯示第n幅畫(huà)面,然后等待一小段時(shí)間,直到下一個(gè)1/24

            結(jié)束

            如果用C語(yǔ)言偽代碼來(lái)描述這一過(guò)程,就是:

            for(i=0; i<n; ++i)

            {

                DrawScene(i);

                Wait();

            }

             

            1、雙緩沖技術(shù)

            在計(jì)算機(jī)上的動(dòng)畫(huà)與實(shí)際的動(dòng)畫(huà)有些不同:實(shí)際的動(dòng)畫(huà)都是先畫(huà)好了,播放的時(shí)候直接拿出來(lái)顯示就行。計(jì)算機(jī)動(dòng)畫(huà)則是畫(huà)一張,就拿出來(lái)一張,再畫(huà)下一張,再拿出來(lái)。如果所需要繪制的圖形很簡(jiǎn)單,那么這樣也沒(méi)什么問(wèn)題。但一旦圖形比較復(fù)雜,繪制需要的時(shí)間較長(zhǎng),問(wèn)題就會(huì)變得突出。

            讓我們把計(jì)算機(jī)想象成一個(gè)畫(huà)圖比較快的人,假如他直接在屏幕上畫(huà)圖,而圖形比較復(fù)雜,則有可能在他只畫(huà)了某幅圖的一半的時(shí)候就被觀眾看到。而后面雖然他把畫(huà)補(bǔ)全了,但觀眾的眼睛卻又沒(méi)有反應(yīng)過(guò)來(lái),還停留在原來(lái)那個(gè)殘缺的畫(huà)面上。也就是說(shuō),有時(shí)候觀眾看到完整的圖象,有時(shí)卻又只看到殘缺的圖象,這樣就造成了屏幕的閃爍。

            如何解決這一問(wèn)題呢?我們?cè)O(shè)想有兩塊畫(huà)板,畫(huà)圖的人在旁邊畫(huà),畫(huà)好以后把他手里的畫(huà)板與掛在屏幕上的畫(huà)板相交換。這樣以來(lái),觀眾就不會(huì)看到殘缺的畫(huà)了。這一技術(shù)被應(yīng)用到計(jì)算機(jī)圖形中,稱為雙緩沖技術(shù)。即:在存儲(chǔ)器(很有可能是顯存)中開(kāi)辟兩塊區(qū)域,一塊作為發(fā)送到顯示器的數(shù)據(jù),一塊作為繪畫(huà)的區(qū)域,在適當(dāng)?shù)臅r(shí)候交換它們。由于交換兩塊內(nèi)存區(qū)域?qū)嶋H上只需要交換兩個(gè)指針,這一方法效率非常高,所以被廣泛的采用。

            注意:雖然絕大多數(shù)平臺(tái)都支持雙緩沖技術(shù),但這一技術(shù)并不是OpenGL標(biāo)準(zhǔn)中的內(nèi)容。OpenGL為了保證更好的可移植性,允許在實(shí)現(xiàn)時(shí)不使用雙緩沖技術(shù)。當(dāng)然,我們常用的PC都是支持雙緩沖技術(shù)的。

            要啟動(dòng)雙緩沖功能,最簡(jiǎn)單的辦法就是使用GLUT工具包。我們以前在main函數(shù)里面寫(xiě):

            glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);

            其中GLUT_SINGLE表示單緩沖,如果改成GLUT_DOUBLE就是雙緩沖了。

            當(dāng)然還有需要更改的地方——每次繪制完成時(shí),我們需要交換兩個(gè)緩沖區(qū),把繪制好的信息用于屏幕顯示(否則無(wú)論怎么繪制,還是什么都看不到)。如果使用GLUT工具包,也可以很輕松的完成這一工作,只要在繪制完成時(shí)簡(jiǎn)單的調(diào)用glutSwapBuffers函數(shù)就可以了。

             

            2、實(shí)現(xiàn)連續(xù)動(dòng)畫(huà)

            似乎沒(méi)有任何疑問(wèn),我們應(yīng)該把繪制動(dòng)畫(huà)的代碼寫(xiě)成下面這個(gè)樣子:

            for(i=0; i<n; ++i)

            {

                DrawScene(i);

                glutSwapBuffers();

                Wait();

            }

            但事實(shí)上,這樣做不太符合窗口系統(tǒng)的程序設(shè)計(jì)思路。還記得我們的第一個(gè)OpenGL程序嗎?我們?cè)?/span>main函數(shù)里寫(xiě):glutDisplayFunc(&myDisplay);

            意思是對(duì)系統(tǒng)說(shuō):如果你需要繪制窗口了,請(qǐng)調(diào)用myDisplay這個(gè)函數(shù)。為什么我們不直接調(diào)用myDisplay,而要采用這種看似舍近求遠(yuǎn)的做法呢?原因在于——我們自己的程序無(wú)法掌握究竟什么時(shí)候該繪制窗口。因?yàn)橐话愕拇翱谙到y(tǒng)——拿我們熟悉一點(diǎn)的來(lái)說(shuō)——WindowsX窗口系統(tǒng),都是支持同時(shí)顯示多個(gè)窗口的。假如你的程序窗口碰巧被別的窗口遮住了,后來(lái)用戶又把原來(lái)遮住的窗口移開(kāi),這時(shí)你的窗口需要重新繪制。很不幸的,你無(wú)法知道這一事件發(fā)生的具體時(shí)間。因此這一切只好委托操作系統(tǒng)來(lái)辦了。

            現(xiàn)在我們?cè)倏瓷厦婺莻€(gè)循環(huán)。既然DrawScene都可以交給操作系統(tǒng)來(lái)代辦了,那讓整個(gè)循環(huán)運(yùn)行起來(lái)的工作是否也可以交給操作系統(tǒng)呢?答案是肯定的。我們先前的思路是:繪制,然后等待一段時(shí)間;再繪制,再等待一段時(shí)間。但如果去掉等待的時(shí)間,就變成了繪制,繪制,……,不停的繪制。——當(dāng)然了,資源是公用的嘛,殺毒軟件總要工作吧?我的下載不能停下來(lái)吧?我的mp3播放還不能給耽擱了。總不能因?yàn)槲覀兊膭?dòng)畫(huà),讓其他的工作都停下來(lái)。因此,我們需要在CPU空閑的時(shí)間繪制。

            這里的CPU空閑的時(shí)間繪制和我們?cè)诘谝徽n講的在需要繪制的時(shí)候繪制有些共通,都是XX時(shí)間做XXGLUT工具包也提供了一個(gè)比較類似的函數(shù):glutIdleFunc,表示在CPU空閑的時(shí)間調(diào)用某一函數(shù)。其實(shí)GLUT還提供了一些別的函數(shù),例如在鍵盤(pán)按下時(shí)做某事等。

             

            到現(xiàn)在,我們已經(jīng)可以初步開(kāi)始制作動(dòng)畫(huà)了。好的,就拿上次那個(gè)太陽(yáng)、地球和月亮的程序開(kāi)刀,讓地球和月亮自己動(dòng)起來(lái)。

             

            Code:


            #include <GL/glut.h>

            // 太陽(yáng)、地球和月亮

            // 假設(shè)每個(gè)月都是30

            // 一年12個(gè)月,共是360

            static int day = 200; // day的變化:從0359

            void myDisplay(void)

            {

                /****************************************************

                 這里的內(nèi)容照搬上一課的,只因?yàn)槭褂昧穗p緩沖,補(bǔ)上最后這句

                *****************************************************/

                glutSwapBuffers();

            }

             

            void myIdle(void)

            {

                /* 新的函數(shù),在空閑時(shí)調(diào)用,作用是把日期往后移動(dòng)一天并重新繪制,達(dá)到動(dòng)畫(huà)效果 */

                ++day;

                if( day >= 360 )

                    day = 0;

                myDisplay();

            }

             

            int main(int argc, char *argv[])

            {

                glutInit(&argc, argv);

                glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); // 修改了參數(shù)為GLUT_DOUBLE

                glutInitWindowPosition(100, 100);

                glutInitWindowSize(400, 400);

                glutCreateWindow("太陽(yáng),地球和月亮");   // 改了窗口標(biāo)題

                glutDisplayFunc(&myDisplay);

                glutIdleFunc(&myIdle);               // 新加入了這句

                glutMainLoop();

                return 0;

            }

            3、關(guān)于垂直同步

            代碼是寫(xiě)好了,但相信大家還有疑問(wèn)。某些朋友可能在運(yùn)行時(shí)發(fā)現(xiàn),雖然CPU幾乎都用上了,但運(yùn)動(dòng)速度很快,根本看不清楚,另一些朋友在運(yùn)行時(shí)發(fā)現(xiàn)CPU使用率很低,根本就沒(méi)有把空閑時(shí)間完全利用起來(lái)。但對(duì)于上面那段代碼來(lái)說(shuō),這些現(xiàn)象都是合理的。這里就牽涉到關(guān)于垂直同步的問(wèn)題。

            大家知道顯示器的刷新率是比較有限的,一般為60~120Hz,也就是一秒鐘刷新60~120次。但如果叫計(jì)算機(jī)繪制一個(gè)簡(jiǎn)單的畫(huà)面,例如只有一個(gè)三角形,則一秒鐘可以繪制成千上萬(wàn)次。因此,如果最大限度的利用計(jì)算機(jī)的處理能力,繪制很多幅畫(huà)面,但顯示器的刷新速度卻跟不上,這不僅造成性能的浪費(fèi),還可能帶來(lái)一些負(fù)面影響(例如,顯示器只刷新到一半時(shí),需要繪制的內(nèi)容卻變化了,由于顯示器是逐行刷新的,于是顯示器上半部分和下半部分實(shí)際上是來(lái)自兩幅畫(huà)面)。采用垂直同步技術(shù)可以解決這一問(wèn)題。即,只有在顯示器刷新時(shí),才把繪制好的圖象傳輸出去供顯示。這樣一來(lái),計(jì)算機(jī)就不必去繪制大量的根本就用不到的圖象了。如果顯示器的刷新率為85Hz,則計(jì)算機(jī)一秒鐘只需要繪制85幅圖象就足夠,如果場(chǎng)景足夠簡(jiǎn)單,就會(huì)造成比較多的CPU空閑。

            幾乎所有的顯卡都支持垂直同步這一功能。

            垂直同步也有它的問(wèn)題。如果刷新頻率為60Hz,則在繪制比較簡(jiǎn)單的場(chǎng)景時(shí),繪制一幅圖畫(huà)需要的時(shí)間很段,幀速可以恒定在60FPS(即60/秒)。如果場(chǎng)景變得復(fù)雜,繪制一幅圖畫(huà)的時(shí)間超過(guò)了1/60秒,則幀速將急劇下降。

            如果繪制一幅圖畫(huà)的時(shí)間為1/50,則在第一個(gè)1/60秒時(shí),顯示器需要刷新了,但由于新的圖畫(huà)沒(méi)有畫(huà)好,所以只能顯示原來(lái)的圖畫(huà),等到下一個(gè)1/60秒時(shí)才顯示新的圖畫(huà)。于是顯示一幅圖畫(huà)實(shí)際上用了1/30秒,幀速為30FPS。(如果不采用垂直同步,則幀速應(yīng)該是50FPS

            如果繪制一幅圖畫(huà)的時(shí)間更長(zhǎng),則下降的趨勢(shì)就是階梯狀的:60FPS30FPS20FPS……60/160/260/3……

            如果每一幅圖畫(huà)的復(fù)雜程度是不一致的,且繪制它們需要的時(shí)間都在1/60上下。則在1/60時(shí)間內(nèi)畫(huà)完時(shí),幀速為60FPS,在1/60時(shí)間未完成時(shí),幀速為30FPS,這就造成了幀速的跳動(dòng)。這是很麻煩的事情,需要避免它——要么想辦法簡(jiǎn)化每一畫(huà)面的繪制時(shí)間,要么都延遲一小段時(shí)間,以作到統(tǒng)一。

            回過(guò)頭來(lái)看前面的問(wèn)題。如果使用了大量的CPU而且速度很快無(wú)法看清,則打開(kāi)垂直同步可以解決該問(wèn)題。當(dāng)然如果你認(rèn)為垂直同步有這樣那樣的缺點(diǎn),也可以關(guān)閉它。——至于如何打開(kāi)和關(guān)閉,因操作系統(tǒng)而異了。具體步驟請(qǐng)自己搜索之。

            當(dāng)然,也有其它辦法可以控制動(dòng)畫(huà)的幀速,或者盡量讓動(dòng)畫(huà)的速度盡量和幀速無(wú)關(guān)。不過(guò)這里面很多內(nèi)容都是與操作系統(tǒng)比較緊密的,況且它們跟OpenGL關(guān)系也不太大。這里就不做介紹了。

            4、計(jì)算幀速

            不知道大家玩過(guò)3D Mark這個(gè)軟件沒(méi)有,它可以運(yùn)行各種場(chǎng)景,測(cè)出幀速,并且為你的系統(tǒng)給出評(píng)分。這里我也介紹一個(gè)計(jì)算幀速的方法。

            根據(jù)定義,幀速就是一秒鐘內(nèi)播放的畫(huà)面數(shù)目(FPS)。我們可以先測(cè)量繪制兩幅畫(huà)面之間時(shí)間t,然后求它的倒數(shù)即可。假如t=0.05s,則FPS的值就是1/0.05=20

            理論上是如此了,可是如何得到這個(gè)時(shí)間呢?通常C語(yǔ)言的time函數(shù)精確度一般只到一秒,肯定是不行了。clock函數(shù)也就到十毫秒左右,還是有點(diǎn)不夠。因?yàn)?/span>FPS60FPS100的時(shí)候,t的值都是十幾毫秒。

            你知道如何測(cè)量一張紙的厚度嗎?一個(gè)粗略的辦法就是:用很多張紙疊在一起測(cè)厚度,計(jì)算平均值就可以了。我們這里也可以這樣辦。測(cè)量繪制50幅畫(huà)面(包括垂直同步等因素的等待時(shí)間)需要的時(shí)間t',由t'=t*50很容易的得到FPS=1/t=50/t'

            下面這段代碼可以統(tǒng)計(jì)該函數(shù)自身的調(diào)用頻率,(原理就像上面說(shuō)的那樣),程序并不復(fù)雜,并且這并不屬于OpenGL的內(nèi)容,所以我不打算詳細(xì)講述它。

            Code:


            #include <time.h>

            double CalFrequency()

            {

                static int count;

                static double save;

                static clock_t last, current;

                double timegap;

             

                ++count;

                if( count <= 50 )

                    return save;

                count = 0;

                last = current;

                current = clock();

                timegap = (current-last)/(double)CLK_TCK;

                save = 50.0/timegap;

                return save;

            }


             

            最后,要把計(jì)算的幀速顯示出來(lái),但我們并沒(méi)有學(xué)習(xí)如何使用OpenGL把文字顯示到屏幕上。——但不要忘了,在我們的圖形窗口背后,還有一個(gè)命令行窗口~使用printf函數(shù)就可以輕易的輸出文字了。

            #include <stdio.h>

            double FPS = CalFrequency();

            printf("FPS = %f\n", FPS);

            最后的一步,也被我們解決了——雖然做法不太雅觀,沒(méi)關(guān)系,以后我們還會(huì)改善它的。

            時(shí)間過(guò)得太久,每次給的程序都只是一小段,一些朋友難免會(huì)出問(wèn)題。

            現(xiàn)在,我給出一個(gè)比較完整的程序,供大家參考。

            Code:


            #include <GL/glut.h>

            #include <stdio.h>

            #include <time.h>

             

            // 太陽(yáng)、地球和月亮

            // 假設(shè)每個(gè)月都是12

            // 一年12個(gè)月,共是360

            static int day = 200; // day的變化:從0359

             

            double CalFrequency()

            {

                static int count;

                static double save;

                static clock_t last, current;

                double timegap;

             

                ++count;

                if( count <= 50 )

                    return save;

                count = 0;

                last = current;

                current = clock();

                timegap = (current-last)/(double)CLK_TCK;

                save = 50.0/timegap;

                return save;

            }

             

            void myDisplay(void)

            {

                double FPS = CalFrequency();

                printf("FPS = %f\n", FPS);

             

                glEnable(GL_DEPTH_TEST);

                glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

             

                glMatrixMode(GL_PROJECTION);

                glLoadIdentity();

                gluPerspective(75, 1, 1, 400000000);

                glMatrixMode(GL_MODELVIEW);

                glLoadIdentity();

                gluLookAt(0, -200000000, 200000000, 0, 0, 0, 0, 0, 1);

             

                // 繪制紅色的太陽(yáng)

                glColor3f(1.0f, 0.0f, 0.0f);

                glutSolidSphere(69600000, 20, 20);

                // 繪制藍(lán)色的地球

                glColor3f(0.0f, 0.0f, 1.0f);

                glRotatef(day/360.0*360.0, 0.0f, 0.0f, -1.0f);

                glTranslatef(150000000, 0.0f, 0.0f);

                glutSolidSphere(15945000, 20, 20);

                // 繪制黃色的月亮

                glColor3f(1.0f, 1.0f, 0.0f);

                glRotatef(day/30.0*360.0 - day/360.0*360.0, 0.0f, 0.0f, -1.0f);

                glTranslatef(38000000, 0.0f, 0.0f);

                glutSolidSphere(4345000, 20, 20);

             

                glFlush();

                glutSwapBuffers();

            }

             

            void myIdle(void)

            {

                ++day;

                if( day >= 360 )

                    day = 0;

                myDisplay();

            }

             

            int main(int argc, char *argv[])

            {

                glutInit(&argc, argv);

                glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);

                glutInitWindowPosition(100, 100);

                glutInitWindowSize(400, 400);

                glutCreateWindow("太陽(yáng),地球和月亮");

                glutDisplayFunc(&myDisplay);

                glutIdleFunc(&myIdle);

                glutMainLoop();

                return 0;

            }


             

            小結(jié):

            OpenGL動(dòng)畫(huà)和傳統(tǒng)意義上的動(dòng)畫(huà)相似,都是把畫(huà)面一幅一幅的呈現(xiàn)在觀眾面前。一旦畫(huà)面變換的速度快了,觀眾就會(huì)認(rèn)為畫(huà)面是連續(xù)的。

            雙緩沖技術(shù)是一種在計(jì)算機(jī)圖形中普遍采用的技術(shù),絕大多數(shù)OpenGL實(shí)現(xiàn)都支持雙緩沖技術(shù)。

            通常都是利用CPU空閑的時(shí)候繪制動(dòng)畫(huà),但也可以有其它的選擇。

            介紹了垂直同步的相關(guān)知識(shí)。

            介紹了一種簡(jiǎn)單的計(jì)算幀速(FPS)的方法。

            最后,我們列出了一份完整的天體動(dòng)畫(huà)程序清單。

             

            =====================   第六課    =====================

            posted on 2010-09-27 14:52 王秋林 閱讀(1031) 評(píng)論(0)  編輯 收藏 引用

            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


            <2010年9月>
            2930311234
            567891011
            12131415161718
            19202122232425
            262728293012
            3456789

            常用鏈接

            留言簿(1)

            隨筆檔案(15)

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            中文字幕精品久久| 久久www免费人成精品香蕉| 久久久受www免费人成| 国产精品久久久久久搜索| 久久人人爽人人精品视频| 精品999久久久久久中文字幕| 精品久久亚洲中文无码| 国产精品成人久久久| 久久亚洲sm情趣捆绑调教| 婷婷久久综合九色综合九七| 久久夜色精品国产亚洲av| 久久综合给合综合久久| 亚洲午夜久久久| 一级做a爰片久久毛片毛片| 精品综合久久久久久98| 综合网日日天干夜夜久久| 综合久久国产九一剧情麻豆| 日韩AV无码久久一区二区| 精品久久久久香蕉网| 久久久精品免费国产四虎| 久久综合狠狠色综合伊人| 国产精久久一区二区三区| 国产三级久久久精品麻豆三级 | 欧美午夜A∨大片久久| 亚洲欧洲精品成人久久曰影片 | 伊人久久大香线蕉综合Av| 久久狠狠高潮亚洲精品| 久久精品国产秦先生| 久久国产乱子伦精品免费午夜| 要久久爱在线免费观看| 亚洲国产另类久久久精品黑人| 99久久免费国产精精品| 国产一区二区精品久久凹凸 | 亚洲av日韩精品久久久久久a| 国产一级持黄大片99久久| 久久国产精品二国产精品| 精品久久久久久无码不卡| 国内精品久久久久久99蜜桃| 久久精品成人影院| 久久国产精品一国产精品金尊| 国产真实乱对白精彩久久|