第二章:Hello World幻燈片 在前一章中,我給出了圖像管線的整體概述。現在是時候將它付諸行動了。在我嘗試渲染任何有趣的3D場景之前,我將像標準的教程那樣使用一個簡單的,二維的"hello world"程序來展示基本的OpenGl API。我們將使用下面這張圖: 將它畫到一個大小合適的窗口中。但是靜態圖片看起來很無趣--我們將它弄得有趣一點,讓它和下面這張圖一起進行反復地淡入淡出: 盡管仍然不是一個很有趣的程序,但是它非常簡單,更復雜的程序使用的OpenGL特性它幾乎都會使用到。完整的代碼上傳到了Github這里。僅僅將圖片畫到屏幕上,這個程序使用了380行C代碼以及一些的著色器代碼,看起來有點過。不過,它將是接下來的一些更有趣的demo的基礎。源文件hello-gl.c中包含了OpenGL渲染的代碼部分,而util.c中包含了很無聊的公共函數用于讀取TGA格式圖像。我在其中包含了兩個這種格式的圖像,hello1.tga和hello2.tga,因為這種格式很容易解析而不用依賴外部庫。我們的著色器代碼在兩個文件中:hello-gl.v.glsl用于頂點著色器,hello-gl.f.glsl用于像素著色器。 在這一章中,我將解釋hello-gl程序的各個部分是如何使用OpenGL API將數據放到圖像管線中以及如何讓它運行起來。當我們討論著色器時,我還將給出一個簡明的GLSL語言的概述。將這些東西全部放在一篇文章中有點多,所以我將這一章拆成四個部分。在這第一部分中,我們將通過GLUT打開一個窗口。在第二部分中,我們將設置我們程序中使用的緩沖區以及紋理對象,它們包含了原始的頂點和圖像數據。之后,我們將寫著色器代碼來處理這些數據將我們最終的圖像顯示在屏幕上。既然我們的游戲的計劃已經布置好了,下面讓我們演員們登場。我們首先設置好GLUT并在屏幕中創建一個空的窗口。 OpenGL頭文件 #include #include #ifdef __APPLE__ # include #else # include #endif 不同的平臺將它們的OpenGL頭文件放在不同地方,但是有了GLEW,你不需要擔心這些。不管它們在哪里,包含GL/glew.h將會為你包含進系統的OpenGL頭文件。不幸的是,包含GLUT仍然有些坑,需要你手動設置一些跨平臺的東西。它的頭文件一般在GL/glut.h,但是MacOS X的附帶的GLUT框架使用蘋果自己的頭文件約定,將GLUT頭文件放在GLUT/glut.h。最近版本的Visual Studio的標準C頭文件與glut的交互方式有個bug,導致包含stdlib.h的順序要在glut.h之前。(譯者注:原文時間約為2010年4月,我不確定現在版本的VS是否還有這個問題) 用GLUT設置好我們的窗口 int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); glutInitWindowSize(400, 300); glutCreateWindow("Hello World"); glutDisplayFunc(&render); glutIdleFunc(&update_fade_factor); GLUT提供了一個有限制的,但是直觀并且可移植的窗口系統接口。通過調用glutInit準備好GLUT之后,我們使用glutInitDisplayMode來指定我們默認的framebuffer應該使用怎樣的緩沖區。在這里,一個雙緩沖(GLUTDOUBLE)的顏色緩沖區(GLUTRGB)足夠了。(Double buffering為framebuffer提供了兩個顏色緩沖區,每一幀中一個用于顯示在屏幕上,另一個畫,在兩者中切換,這樣動畫看起來就是平滑的)。如果我們需要深度或者 stencil 緩沖區,我們可以在這里做。然后我們使用glutIninWindowSize設置初始的圖片的窗口大小為400x300并使用glutCreateWindon創建窗口。最后,我們指定兩個回調函數接受窗口事件:一個glutDisplayFunc用于在窗口需要顯示時渲染我們的圖像,一個glutIdleFunc持續地更新兩個圖片之間隨時間漸變的因子。 glewInit(); if (!GLEW_VERSION_2_0) { fprintf(stderr, "OpenGL 2.0 not available\n"); return 1; } 在GLUT創建我們的窗口之后,它會準備好OpenGL,這樣我們就能開始調用這個庫。我們首先要做的是初始化GLEW。當函數glewInit被調用時,它會根據OpenGL的可用版本以及擴展設置一系列的標志。這里在繼續之前我們檢測GLEWVERSION2_0標記以確保OpenGL 2.0是可用的。除了設置版本標記,GLEW幾乎不扮演其它任何角色,并且在它初始化之后我們不需要與它交互。 if (!make_resources()) { fprintf(stderr, "Failed to load resources\n"); return 1; } glutMainLoop(); return 0; } GLEW初始化后,我們調用我們的make_resources函數來設置我們的OpenGL資源。如果我們的資源加載成功,glutMainLoop會運行。它顯示窗口,開始從窗口系統接收UI事件,并調用我們設置好的回調函數響應這些事件。它還會在用戶退出時幫我們退出程序。return 0僅僅是避免編譯警告,實際上絕對不會運行到那里。 編譯并運行我們的程序 現在我們可以先不管GLUT回調函數和make_resources函數,得到一個可以運行的,盡管沒什么價值的程序: static int make_resources(void) { return 1; } static void update_fade_factor(void) { } static void render(void) { glClearColor(1.0f, 1.0f, 1.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glutSwapBuffers(); } glClearColor設置一個RGBA消除顏色(這里是白色),然后glClear使用這個顏色來填充framebuffer的顏色緩沖區。glutSwapBuffers然后將我們的用于消除的顏色緩沖區顯示在屏幕上。通過這幾個函數,現在我們可以編譯和運行我們的程序。這個空函數版本在Github倉庫中對應的是hello-gl-dummy.c。編譯程序并鏈接到OpenGL,GLUT和GLEW庫的命令在不同平臺下會有些區別。在大多數類Unix下大概像下面這樣子: gcc -o hello-gl-dummy hello-gl-dummy.c \ -I/usr/X11R6/include -L/usr/X11R6/lib \ -lGL -lGLEW -lglut 在MacOS X中: # Assuming GLEW was installed to /opt/local gcc -o hello-gl-dummy hello-gl-dummy.c \ -I/opt/local/include -L/opt/local/lib \ -framework OpenGL -framework GLUT -lGLEW 在Windows中使用Visual C++: cl /Fohello-gl-dummy.obj /c hello-gl-dummy.c link /out:hello-gl-dummy.exe hello-gl-dummy.obj \ opengl32.lib glut32.lib glew32.lib 在Windows中使用mingw: gcc -o hello-gl-dummy.exe hello-gl-dummy.c \ -lopengl32 -lglut32 -lglew32 在Github倉庫中還包含了各個平臺下的makefile。你可以使用hello-gl-dummy(或者hello-gl-dummy.exe,在Windows下)編譯這個版本的程序: make -f Makefile.MacOSX hello-gl-dummy # or Makefile.Unix or Makefile.Mingw nmake /f Nmakefile.Windows hello-gl-dummy.exe 一旦你編譯好程序,你應該能夠運行它并且得到一個白色的窗口: 關閉窗口,或者在MacOS X下退出應該可以關閉它。 接下來,緩沖以及紋理 我們已經準備好了將我們的頂點和圖像放到OpenGL。在下一篇文章中,我們會介紹OpenGL的緩沖以及紋理對象。 |