紋理只是一種能夠應(yīng)用到場景中的三角形上的圖像數(shù)據(jù)瞧剖,它通過經(jīng)過過濾的紋理單元(texel,相當(dāng)于紋理的像素)填充到實心區(qū)域。
原始圖像數(shù)據(jù)
像素包裝
- 圖形數(shù)據(jù)在內(nèi)存中很少以緊密包裝的形式存在,為了性能,一副圖像的每一行都應(yīng)該從一個特定字節(jié)對齊地址開始(犧牲了空間)梯找,絕大多數(shù)編譯器會自動把變量和緩沖區(qū)放置在一個針對該架構(gòu)對齊優(yōu)化的地址上
- Windows中的RMP文件格式的像素數(shù)據(jù)使用4字節(jié)排列;然而Targa(TGA)文件格式則是1個字節(jié)排列的(這樣不會浪費空間)
改變或恢復(fù)像素的儲存方式:
void glPixelStorei(GLenum pname,GLint param);
void glPixelStoref(GLenum pname,GLfloat param);
如圖我們想要改成緊密包裝像素數(shù)據(jù)益涧,應(yīng)該調(diào)用glPixelStorei(GL_UNPACK_ALIGNMENT,1);//GL_UNPACK_ALIGNMENT制定ruhr從數(shù)據(jù)緩沖區(qū)中解包圖像數(shù)據(jù)
像素圖
- 像素圖在內(nèi)存布局上與位圖非常相似锈锤,但是每個像素都需要一個以上的儲存位來表示。每個像素的附加位允許儲存強度(intensity,有時被陳偉亮度久免,即luminance的值)或者顏色分量值浅辙。
- 可以使用下面的函數(shù)將顏色緩沖區(qū)的內(nèi)容作為像素圖直接讀取.
void glReadPixels(GLint x,GLint y,GLSizei width,GLSizei height,
GLenum format,GLenum type,const void *pixels);
/*
x,y制定為舉行左下角的窗口坐標(biāo),然后制定矩形的width和height值(像素形式)阎姥。如果顏色緩沖區(qū)儲存的數(shù)據(jù)與我們要求的不同记舆,OpenGL將負(fù)責(zé)進行必要的轉(zhuǎn)換。
*pixels必須是合法的
format制定piels指向的數(shù)據(jù)元素的顏色布局
type解釋參數(shù)*pixels指向的數(shù)據(jù)呼巴,他告訴OpenGL使用緩存區(qū)中的什么數(shù)據(jù)類型來存儲顏色分量
*/
glReadPixels從圖形硬件中復(fù)制數(shù)據(jù)泽腮,通常通過總線傳輸?shù)较到y(tǒng)內(nèi)存
包裝的像素格式
包裝的像素格式將顏色數(shù)據(jù)壓縮到了盡可能少的儲存位中,每個顏色通道的位數(shù)顯示在常量中
對于glReadPixels函數(shù)來說衣赶,讀取操作在雙緩沖區(qū)的渲染環(huán)境下載后臺緩沖區(qū)進行诊赊,在單緩沖區(qū)渲染環(huán)境下則在前臺緩沖區(qū)進行
可以用void glReadBuffer(GLenum mode);
改變這些像素操作的源
保存像素
用glWriteTGA函數(shù)來講屏幕圖像保存為一個Targa文件
讀取像素
讀取Targa文件以備OpenGL使用的函數(shù)
載入紋理
有三個OpenGL函數(shù)最經(jīng)常用來從存儲器緩沖區(qū)中載入(比如說,從一個磁盤文件中讀雀椤)紋理數(shù)據(jù):
void glTexImage1D((GLenum target, GLint level, GLenum internalformat,GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type,void* data);
void glTexImage2D((GLenum target, GLint level, GLenum internalformat,GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type,void* data);
void glTexImage3D((GLenum target, GLint level, GLenum internalformat,GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type,void* data)
target 指定目標(biāo)紋理碧磅,這個值必須是GL_TEXTURE_2D。
level 執(zhí)行細(xì)節(jié)級別遵馆。0是最基本的圖像級別鲸郊,n表示第N級貼圖細(xì)化級別。
internalformat 指定紋理中的顏色組件团搞⊙贤可選的值有GL_ALPHA,GL_RGB,GL_RGBA,GL_LUMINANCE, GL_LUMINANCE_ALPHA 等幾種多艇。
width 指定紋理圖像的寬度逻恐,必須是2的n次方。紋理圖片至少要支持64個材質(zhì)元素的寬度
height 指定紋理圖像的高度峻黍,必須是2的m次方复隆。紋理圖片至少要支持64個材質(zhì)元素的高度
border 指定邊框的寬度。必須為0姆涩。
format 像素數(shù)據(jù)的顏色格式, 不需要和internalformatt取值必須相同挽拂。可選的值參考internalformat骨饿。
type 指定像素數(shù)據(jù)的數(shù)據(jù)類型亏栈。可以使用的值有GL_UNSIGNED_BYTE,GL_UNSIGNED_SHORT_5_6_5,GL_UNSIGNED_SHORT_4_4_4_4,GL_UNSIGNED_SHORT_5_5_5_1
最后三個參數(shù)format, type, data和用于把圖像放入顏色緩沖區(qū)的glDrawPixels函數(shù)的對應(yīng)參數(shù)相同
使用顏色緩沖區(qū)
一維和二維紋理也可以從顏色緩沖區(qū)加載數(shù)據(jù)宏赘,我們可以從顏色緩沖區(qū)讀取一幅圖像绒北,并通過下面這兩個函數(shù)將它作為一個新的紋理使用。
void glCopyTexImage1D( GLenum target,
GLint level,
GLenum internalformat,
GLint x,
GLint y,
GLsizei width,
GLint border);
void glCopyTexImage2D( GLenum target,
GLint level,
GLenum internalformat,
GLint x,
GLint y,
GLsizei width,
GLint border);
更新紋理
替換紋理通常比重新加載一個新紋理快得多察署,用于完成這個任務(wù)的是glTexSubImage()
闷游。
glCopyTexSubImage
函數(shù)允許從顏色緩沖區(qū)讀取紋理,并插入或替換原來紋理的一部分。
顏色緩沖區(qū)是2D的脐往,不存在一種對應(yīng)方法來講一副2D彩色圖像作為一個3D紋理的來源休吠,但我們可以使用glCopyTexSubImage3D函數(shù),在一個三位問李重使用顏色緩沖區(qū)的數(shù)據(jù)來設(shè)置它的一個紋理單元平面
紋理對象
在紋理之間進行切換或者重新加載不同的紋理圖像開銷很大业簿。紋理對象支持我們一次加載一個以上的紋理狀態(tài)瘤礁,以及在它們之間的快速切換。
void glGenTextures(GLsizei n,GLunit *textures)
分配一些紋理對象
void glBindTextures(GLenum n,GLunit texture)
綁定一種紋理狀態(tài)
void glDeleteTextures(GLsizei n,GLunit *textures)
刪除紋理對象
GLboolean glIsTexture(GLuint texture)
對紋理對象名進行測試梅尤,以判斷它們是否有效
紋理應(yīng)用
加載紋理只是在幾何圖形上應(yīng)用紋理的第一部蔚携。最低限度我們必須同時提供紋理坐標(biāo),并設(shè)置紋理坐標(biāo)環(huán)繞模式和紋理過濾克饶。最后酝蜒,我們可以選擇對紋理進行Mip貼圖,以提高紋理貼圖性能和/或視覺質(zhì)量
紋理坐標(biāo)
紋理坐標(biāo)是x和y軸上0到1之間的范圍(注意我們使用的是2D紋理圖片)矾湃。使用紋理坐標(biāo)獲取紋理顏色叫做采樣(Sampling)亡脑。紋理坐標(biāo)起始于(0,0)也就是紋理圖片的左下角,終結(jié)于紋理圖片的右上角(1,1)邀跃。下面的圖片展示了我們是如何把紋理坐標(biāo)映射到三角形上的霉咨。
我們?yōu)槿切螠?zhǔn)備了3個紋理坐標(biāo)點。如上圖所示拍屑,我們希望三角形的左下角對應(yīng)紋理的左下角途戒,因此我們把三角左下角的頂點的紋理坐標(biāo)設(shè)置為(0,0);三角形的上頂點對應(yīng)于圖片的中間所以我們把它的紋理坐標(biāo)設(shè)置為(0.5,1.0)僵驰;同理右下方的頂點設(shè)置為(1.0,0)喷斋。我們只要傳遞這三個紋理坐標(biāo)給頂點著色器就行了,接著片段著色器會為每個片段生成紋理坐標(biāo)的插值蒜茴。
紋理坐標(biāo)看起來就像這樣:
GLfloat texCoords[] = {
0.0f, 0.0f, // 左下角
1.0f, 0.0f, // 右下角
0.5f, 1.0f // 頂部位置
};
如圖星爪,一維、二維粉私、三維紋理坐標(biāo)(s顽腾、t、r诺核、q與x抄肖、y、z窖杀、w相類似)(q為縮放因子)
紋理參數(shù)
glTexParameterf(GLenum target,GLenum pname,GLfloat param);
glTexParameteri(GLenum target,GLenum pname,GLint param);
glTexParameterfv(GLenum target,GLenum pname,GLfloat * params);
glTexParameteriv(GLenum target,GLenum pname,GLint *params);
基本過濾
紋理過濾:根據(jù)一個拉伸或收縮的紋理貼圖計算顏色偏短的過程稱為紋理過濾
最近鄰過濾是我們能夠選擇的最簡單漓摩、最快速的過濾方法,其最顯著的特征就是當(dāng)紋理被拉伸到特別大的時候所出現(xiàn)的大片斑駁狀像素陈瘦。
//最近鄰過濾用于GL_TEXTURE_2D,為放大和縮小過濾器設(shè)置紋理過濾器
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAX_FILTER,GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST)
線性過濾:更接近真實幌甘,沒有人工操作的痕跡
//簡單設(shè)置線性過濾
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAX_FILTER,GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR)
紋理環(huán)繞
紋理采樣有幾種不同的插值方式潮售。我們需要自己告訴OpenGL在紋理中采用哪種采樣方式。
- 紋理坐標(biāo)通常的范圍是從(0, 0)到(1, 1)锅风,如果我們把紋理坐標(biāo)設(shè)置為范圍以外會發(fā)生什么酥诽?OpenGL默認(rèn)的行為是重復(fù)這個紋理圖像(我們簡單地忽略浮點紋理坐標(biāo)的整數(shù)部分),但OpenGL提供了更多的選擇:
環(huán)繞方式:
- GL_REPEAT 紋理的默認(rèn)行為皱埠,重復(fù)紋理圖像
- GL_MIRRORED_REPEAT和GL_REPEAT一樣肮帐,除了重復(fù)的圖片是鏡像放置的
- GL_CLAMP_TO_EDGE紋理坐標(biāo)會在0到1之間,超出的部分會重復(fù)紋理坐標(biāo)的邊緣边器,就是邊緣被拉伸
- GL_CLAMP_TO_BORDER超出的部分是用戶指定的邊緣的顏色
當(dāng)紋理坐標(biāo)超出默認(rèn)范圍時训枢,每個值都有不同的視覺效果輸出。我們來看看這些紋理圖像的例子:
前面提到的選項都可以使用glTexParameter
函數(shù)單獨設(shè)置每個坐標(biāo)軸s忘巧、t(如果是使用3D紋理那么還有一個r)它們和x恒界、y(z)是相等的:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
第一個參數(shù)指定了紋理目標(biāo);我們使用的是2D紋理砚嘴,因此紋理目標(biāo)是GL_TEXTURE_2D十酣。
第二個參數(shù)需要我們?nèi)ジ嬷覀兿MピO(shè)置哪個紋理軸。我們打算設(shè)置的是WRAP選項际长,并且指定S和T軸耸采。最后一個參數(shù)需要我們傳遞放置方式掐禁,在這個例子里我們在當(dāng)前激活紋理上應(yīng)用GL_MIRRORED_REPEAT忠怖。
如果我們選擇GL_CLAMP_TO_BORDER選項,我們還要指定一個邊緣的顏色簇搅。這次使用glTexParameter
函數(shù)的fv后綴形式如绸,加上GL_TEXTURE_BORDER_COLOR作為選項嘱朽,這個函數(shù)需要我們傳遞一個邊緣顏色的float數(shù)組作為顏色值:
float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
綜合應(yīng)用
// Pyramid.cpp
// OpenGL SuperBible, Chapter 5
// Demonstrates Texture mapping a pyramid
// Program by Richard S. Wright Jr.
#include <GLTools.h> // OpenGL toolkit
#include <GLMatrixStack.h>
#include <GLFrame.h>
#include <GLFrustum.h>
#include <GLBatch.h>
#include <GLGeometryTransform.h>
#include <math.h>
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
/////////////////////////////////////////////////////////////////////////////////
// An assortment of needed classes
GLShaderManager shaderManager;
GLMatrixStack modelViewMatrix;
GLMatrixStack projectionMatrix;//投影矩陣堆棧
GLFrame cameraFrame;//全局照相機實例
GLFrame objectFrame;
GLFrustum viewFrustum;//投影矩陣
GLBatch pyramidBatch;
GLuint textureID;
GLGeometryTransform transformPipeline;// 變換管線
M3DMatrix44f shadowMatrix;
void MakePyramid(GLBatch& pyramidBatch)
{
//6個三角形18個頂點,1代表在這個批次中將應(yīng)用一個紋理
pyramidBatch.Begin(GL_TRIANGLES, 18, 1);
// Bottom of pyramid
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);//向批次中添加一個表面法線竭沫,代表表面(頂點)面對的方向燥翅。在大多數(shù)光照模式下是必須的
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);//添加一個紋理坐標(biāo)
pyramidBatch.Vertex3f(-1.0f, -1.0f, -1.0f);//添加了頂點的位置
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3f(1.0f, -1.0f, -1.0f);
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
pyramidBatch.Vertex3f(1.0f, -1.0f, 1.0f);
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
pyramidBatch.Vertex3f(-1.0f, -1.0f, 1.0f);
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3f(-1.0f, -1.0f, -1.0f);
pyramidBatch.Normal3f(0.0f, -1.0f, 0.0f);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
pyramidBatch.Vertex3f(1.0f, -1.0f, 1.0f);
M3DVector3f vApex = { 0.0f, 1.0f, 0.0f }; // Vector of three floats(x, y, z)
M3DVector3f vFrontLeft = { -1.0f, -1.0f, 1.0f };
M3DVector3f vFrontRight = { 1.0f, -1.0f, 1.0f };
M3DVector3f vBackLeft = { -1.0f, -1.0f, -1.0f };
M3DVector3f vBackRight = { 1.0f, -1.0f, -1.0f };
M3DVector3f n;
// Front of Pyramid
//glTool函數(shù)庫包含了一個函數(shù)骑篙,專門用于根據(jù)一個多邊形上的3個點計算一條法線向量:
//該方法的第一個參數(shù)用于存儲求得的法線向量蜕提,還要另外向它傳遞3個向量,表示取自多邊形或三角形上的點
//(以逆時針環(huán)繞方向指定)靶端。注意谎势,該方法返回的法線向量并不一定是單位長度的
m3dFindNormal(n, vApex, vFrontLeft, vFrontRight);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex); // Apex
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontLeft); // Front left corner
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontRight); // Front right corner
m3dFindNormal(n, vApex, vBackLeft, vFrontLeft);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex); // Apex
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackLeft); // Back left corner
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontLeft); // Front left corner
m3dFindNormal(n, vApex, vFrontRight, vBackRight);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex); // Apex
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vFrontRight); // Front right corner
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackRight); // Back right cornder
m3dFindNormal(n, vApex, vBackRight, vBackLeft);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
pyramidBatch.Vertex3fv(vApex); // Apex
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackRight); // Back right cornder
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
pyramidBatch.Vertex3fv(vBackLeft); // Back left corner
pyramidBatch.End();
}
// Load a TGA as a 2D Texture. Completely initialize the state
bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode)
{
GLbyte *pBits;
int nWidth, nHeight, nComponents;
GLenum eFormat;
// Read the texture bits
pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);
if (pBits == NULL)
return false;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, nComponents, nWidth, nHeight, 0,
eFormat, GL_UNSIGNED_BYTE, pBits);
free(pBits);
if (minFilter == GL_LINEAR_MIPMAP_LINEAR ||
minFilter == GL_LINEAR_MIPMAP_NEAREST ||
minFilter == GL_NEAREST_MIPMAP_LINEAR ||
minFilter == GL_NEAREST_MIPMAP_NEAREST)
glGenerateMipmap(GL_TEXTURE_2D);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// This function does any needed initialization on the rendering context.
// This is the first opportunity to do any OpenGL related tasks.
void SetupRC()
{
// Black background
glClearColor(0.7f, 0.7f, 0.7f, 1.0f);
//調(diào)用之前使用
shaderManager.InitializeStockShaders();
glEnable(GL_DEPTH_TEST);
glGenTextures(1, &textureID);//分配一些紋理對象
glBindTexture(GL_TEXTURE_2D, textureID);//綁定一種紋理狀態(tài)
LoadTGATexture("stone.tga", GL_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE);
MakePyramid(pyramidBatch);
cameraFrame.MoveForward(-7.0f);
}
///////////////////////////////////////////////////////////////////////////////
// Cleanup... such as deleting texture objects
void ShutdownRC(void)
{
glDeleteTextures(1, &textureID);
}
///////////////////////////////////////////////////////////////////////////////
// Called to draw scene
void RenderScene(void)
{
static GLfloat vLightPos[] = { 1.0f, 1.0f, 0.0f };
static GLfloat vWhite[] = { 1.0f, 1.0f, 1.0f, 1.0f };
// Clear the window with current clearing color
//glClear()函數(shù)的作用是用當(dāng)前緩沖區(qū)清除值,
//也就是glClearColor或者glClearDepth杨名、glClearIndex脏榆、glClearStencil、glClearAccum等函數(shù)所指定的值來清除指定的緩沖區(qū)台谍,
//也可以使用glDrawBuffer一次清除多個顏色緩存须喂。
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//保存當(dāng)前模型視圖矩陣
modelViewMatrix.PushMatrix();
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
modelViewMatrix.MultMatrix(mCamera);
M3DMatrix44f mObjectFrame;
objectFrame.GetMatrix(mObjectFrame);
modelViewMatrix.MultMatrix(mObjectFrame);
glBindTexture(GL_TEXTURE_2D, textureID);//綁定紋理對象
//繪制背景,點光源
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,
transformPipeline.GetModelViewMatrix(),
transformPipeline.GetProjectionMatrix(),
vLightPos, vWhite, 0);
pyramidBatch.Draw();
modelViewMatrix.PopMatrix();
// Flush drawing commands
glutSwapBuffers();
}
// Respond to arrow keys by moving the camera frame of reference
void SpecialKeys(int key, int x, int y)
{
if (key == GLUT_KEY_UP)
//void RotateWorld(float fAngle, float x, float y, float z)
//在世界坐標(biāo)系中旋轉(zhuǎn)
objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
if (key == GLUT_KEY_DOWN)
objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
if (key == GLUT_KEY_LEFT)
objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
if (key == GLUT_KEY_RIGHT)
objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
glutPostRedisplay();
//glutPostRedisplay 標(biāo)記當(dāng)前窗口需要重新繪制。
//通過glutMainLoop下一次循環(huán)時坞生,窗口顯示將被回調(diào)以重新顯示窗口的正常面板仔役。
}
///////////////////////////////////////////////////////////////////////////////
// Window has changed size, or has just been created. In either case, we need
// to use the window dimensions to set the viewport and the projection matrix.
void ChangeSize(int w, int h)
{
glViewport(0, 0, w, h);
//創(chuàng)建投影矩陣并把它載入到投影矩陣堆棧中
viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 500.0f);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//設(shè)置變換管線以使用兩個矩陣堆棧
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
///////////////////////////////////////////////////////////////////////////////
// Main entry point for GLUT based programs
int main(int argc, char* argv[])
{
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
glutInitWindowSize(800, 600);
glutCreateWindow("Pyramid");
glutReshapeFunc(ChangeSize);
glutSpecialFunc(SpecialKeys);
glutDisplayFunc(RenderScene);//OpenGL渲染代碼
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
SetupRC();
glutMainLoop();
ShutdownRC();
return 0;
}
Mip貼圖
Mip貼圖是一種強大的紋理技巧,不僅可以提高渲染性能是己,而且可以改善場景的顯示質(zhì)量又兵。Mipmap中每一個層級的小圖都是主圖的一個特定比例的縮小細(xì)節(jié)的復(fù)制品。雖然在某些必要的視角卒废,主圖仍然會被使用沛厨,來渲染完整的細(xì)節(jié)。但是當(dāng)貼圖被縮小或者只需要從遠(yuǎn)距離觀看時摔认,mipmap就會轉(zhuǎn)換到適當(dāng)?shù)膶蛹壞嫫ぁ!?/p>
Mip貼圖過濾
活動的Mip貼圖
//漢字注釋見書p151
// Tunnel.cpp
// Demonstrates mipmapping and using texture objects
// OpenGL SuperBible
// Richard S. Wright Jr.
#include <GLTools.h>
#include <GLShaderManager.h>
#include <GLFrustum.h>
#include <GLBatch.h>
#include <GLFrame.h>
#include <GLMatrixStack.h>
#include <GLGeometryTransform.h>
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
GLShaderManager shaderManager; // Shader Manager
GLMatrixStack modelViewMatrix; // Modelview Matrix
GLMatrixStack projectionMatrix; // Projection Matrix
GLFrustum viewFrustum; // View Frustum
GLGeometryTransform transformPipeline; // Geometry Transform Pipeline
GLBatch floorBatch;
GLBatch ceilingBatch;
GLBatch leftWallBatch;
GLBatch rightWallBatch;
GLfloat viewZ = -65.0f;
// Texture objects
#define TEXTURE_BRICK 0
#define TEXTURE_FLOOR 1
#define TEXTURE_CEILING 2
#define TEXTURE_COUNT 3
GLuint textures[TEXTURE_COUNT];
const char *szTextureFiles[TEXTURE_COUNT] = { "brick.tga", "floor.tga", "ceiling.tga" };
///////////////////////////////////////////////////////////////////////////////
// Change texture filter for each texture object
void ProcessMenu(int value)
{
GLfloat fLargest;
GLint iLoop;
for(iLoop = 0; iLoop < TEXTURE_COUNT; iLoop++)
{
glBindTexture(GL_TEXTURE_2D, textures[iLoop]);
switch(value)
{
case 0:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
break;
case 1:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
break;
case 2:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
break;
case 3:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
break;
case 4:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
break;
case 5:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
break;
case 6:
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest);
break;
case 7:
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f);
break;
}
}
// Trigger Redraw
glutPostRedisplay();
}
//////////////////////////////////////////////////////////////////
// This function does any needed initialization on the rendering
// context. Here it sets up and initializes the texture objects.
void SetupRC()
{
GLbyte *pBytes;
GLint iWidth, iHeight, iComponents;
GLenum eFormat;
GLint iLoop;
// Black background
glClearColor(0.0f, 0.0f, 0.0f,1.0f);
shaderManager.InitializeStockShaders();
// Load textures
glGenTextures(TEXTURE_COUNT, textures);
for(iLoop = 0; iLoop < TEXTURE_COUNT; iLoop++)
{
// Bind to next texture object
glBindTexture(GL_TEXTURE_2D, textures[iLoop]);
// Load texture, set filter and wrap modes
pBytes = gltReadTGABits(szTextureFiles[iLoop],&iWidth, &iHeight,
&iComponents, &eFormat);
// Load texture, set filter and wrap modes
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, iComponents, iWidth, iHeight, 0, eFormat, GL_UNSIGNED_BYTE, pBytes);
glGenerateMipmap(GL_TEXTURE_2D);
// Don't need original texture data any more
free(pBytes);
}
// Build Geometry
GLfloat z;
floorBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
for(z = 60.0f; z >= 0.0f; z -=10.0f)
{
floorBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
floorBatch.Vertex3f(-10.0f, -10.0f, z);
floorBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
floorBatch.Vertex3f(10.0f, -10.0f, z);
floorBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
floorBatch.Vertex3f(-10.0f, -10.0f, z - 10.0f);
floorBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
floorBatch.Vertex3f(10.0f, -10.0f, z - 10.0f);
}
floorBatch.End();
ceilingBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
for(z = 60.0f; z >= 0.0f; z -=10.0f)
{
ceilingBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
ceilingBatch.Vertex3f(-10.0f, 10.0f, z - 10.0f);
ceilingBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
ceilingBatch.Vertex3f(10.0f, 10.0f, z - 10.0f);
ceilingBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
ceilingBatch.Vertex3f(-10.0f, 10.0f, z);
ceilingBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
ceilingBatch.Vertex3f(10.0f, 10.0f, z);
}
ceilingBatch.End();
leftWallBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
for(z = 60.0f; z >= 0.0f; z -=10.0f)
{
leftWallBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
leftWallBatch.Vertex3f(-10.0f, -10.0f, z);
leftWallBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
leftWallBatch.Vertex3f(-10.0f, 10.0f, z);
leftWallBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
leftWallBatch.Vertex3f(-10.0f, -10.0f, z - 10.0f);
leftWallBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
leftWallBatch.Vertex3f(-10.0f, 10.0f, z - 10.0f);
}
leftWallBatch.End();
rightWallBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
for(z = 60.0f; z >= 0.0f; z -=10.0f)
{
rightWallBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
rightWallBatch.Vertex3f(10.0f, -10.0f, z);
rightWallBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
rightWallBatch.Vertex3f(10.0f, 10.0f, z);
rightWallBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
rightWallBatch.Vertex3f(10.0f, -10.0f, z - 10.0f);
rightWallBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
rightWallBatch.Vertex3f(10.0f, 10.0f, z - 10.0f);
}
rightWallBatch.End();
}
///////////////////////////////////////////////////
// Shutdown the rendering context. Just deletes the
// texture objects
void ShutdownRC(void)
{
glDeleteTextures(TEXTURE_COUNT, textures);
}
///////////////////////////////////////////////////
// Respond to arrow keys, move the viewpoint back
// and forth
void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP)
viewZ += 0.5f;
if(key == GLUT_KEY_DOWN)
viewZ -= 0.5f;
// Refresh the Window
glutPostRedisplay();
}
/////////////////////////////////////////////////////////////////////
// Change viewing volume and viewport. Called when window is resized
void ChangeSize(int w, int h)
{
GLfloat fAspect;
// Prevent a divide by zero
if(h == 0)
h = 1;
// Set Viewport to window dimensions
glViewport(0, 0, w, h);
fAspect = (GLfloat)w/(GLfloat)h;
// Produce the perspective projection
viewFrustum.SetPerspective(80.0f,fAspect,1.0,120.0);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
///////////////////////////////////////////////////////
// Called to draw scene
void RenderScene(void)
{
// Clear the window with current clearing color
glClear(GL_COLOR_BUFFER_BIT);
modelViewMatrix.PushMatrix();
modelViewMatrix.Translate(0.0f, 0.0f, viewZ);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_REPLACE, transformPipeline.GetModelViewProjectionMatrix(), 0);
glBindTexture(GL_TEXTURE_2D, textures[TEXTURE_FLOOR]);
floorBatch.Draw();
glBindTexture(GL_TEXTURE_2D, textures[TEXTURE_CEILING]);
ceilingBatch.Draw();
glBindTexture(GL_TEXTURE_2D, textures[TEXTURE_BRICK]);
leftWallBatch.Draw();
rightWallBatch.Draw();
modelViewMatrix.PopMatrix();
// Buffer swap
glutSwapBuffers();
}
//////////////////////////////////////////////////////
// Program entry point
int main(int argc, char *argv[])
{
gltSetWorkingDirectory(argv[0]);
// Standard initialization stuff
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(800, 600);
glutCreateWindow("Anisotropic Tunnel");
glutReshapeFunc(ChangeSize);
glutSpecialFunc(SpecialKeys);
glutDisplayFunc(RenderScene);
// Add menu entries to change filter
glutCreateMenu(ProcessMenu);
glutAddMenuEntry("GL_NEAREST",0);
glutAddMenuEntry("GL_LINEAR",1);
glutAddMenuEntry("GL_NEAREST_MIPMAP_NEAREST",2);
glutAddMenuEntry("GL_NEAREST_MIPMAP_LINEAR", 3);
glutAddMenuEntry("GL_LINEAR_MIPMAP_NEAREST", 4);
glutAddMenuEntry("GL_LINEAR_MIPMAP_LINEAR", 5);
glutAddMenuEntry("Anisotropic Filter", 6);
glutAddMenuEntry("Anisotropic Off", 7);
glutAttachMenu(GLUT_RIGHT_BUTTON);
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
// Startup, loop, shutdown
SetupRC();
glutMainLoop();
ShutdownRC();
return 0;
}
各向異性過濾
如果我們在紋理過濾時考慮了觀察角度参袱,那么這種方法就叫做各向異性過濾
紋理壓縮
紋理壓縮(Texture compression)是一種專為在三維計算機圖形渲染系統(tǒng)中存儲紋理而使用的圖像壓縮技術(shù)页屠。與普通圖像壓縮算法的不同之處在于,紋理壓縮算法為紋素的隨機存取做了優(yōu)化蓖柔。
壓縮紋理
加載壓縮紋理
最后一個示例
// SphereWorld.cpp
// OpenGL SuperBible
// New and improved (performance) sphere world
// Program by Richard S. Wright Jr.
#include <GLTools.h>
#include <GLShaderManager.h>
#include <GLFrustum.h>
#include <GLBatch.h>
#include <GLMatrixStack.h>
#include <GLGeometryTransform.h>
#include <StopWatch.h>
#include <math.h>
#include <stdio.h>
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
#define NUM_SPHERES 50
GLFrame spheres[NUM_SPHERES];
GLShaderManager shaderManager; // Shader Manager
GLMatrixStack modelViewMatrix; // Modelview Matrix
GLMatrixStack projectionMatrix; // Projection Matrix
GLFrustum viewFrustum; // View Frustum
GLGeometryTransform transformPipeline; // Geometry Transform Pipeline
GLFrame cameraFrame; // Camera frame
GLTriangleBatch torusBatch;
GLTriangleBatch sphereBatch;
GLBatch floorBatch;
GLuint uiTextures[3];
void DrawSongAndDance(GLfloat yRot) // Called to draw dancing objects
{
static GLfloat vWhite[] = { 1.0f, 1.0f, 1.0f, 1.0f };
static GLfloat vLightPos[] = { 0.0f, 3.0f, 0.0f, 1.0f };
// Get the light position in eye space
M3DVector4f vLightTransformed;
M3DMatrix44f mCamera;
modelViewMatrix.GetMatrix(mCamera);
m3dTransformVector4(vLightTransformed, vLightPos, mCamera);
// Draw the light source
modelViewMatrix.PushMatrix();
modelViewMatrix.Translatev(vLightPos);
shaderManager.UseStockShader(GLT_SHADER_FLAT,
transformPipeline.GetModelViewProjectionMatrix(),
vWhite);
sphereBatch.Draw();
modelViewMatrix.PopMatrix();
glBindTexture(GL_TEXTURE_2D, uiTextures[2]);
for(int i = 0; i < NUM_SPHERES; i++) {
modelViewMatrix.PushMatrix();
modelViewMatrix.MultMatrix(spheres[i]);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,
modelViewMatrix.GetMatrix(),
transformPipeline.GetProjectionMatrix(),
vLightTransformed,
vWhite,
0);
sphereBatch.Draw();
modelViewMatrix.PopMatrix();
}
// Song and dance
modelViewMatrix.Translate(0.0f, 0.2f, -2.5f);
modelViewMatrix.PushMatrix(); // Saves the translated origin
modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
// Draw stuff relative to the camera
glBindTexture(GL_TEXTURE_2D, uiTextures[1]);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,
modelViewMatrix.GetMatrix(),
transformPipeline.GetProjectionMatrix(),
vLightTransformed,
vWhite,
0);
torusBatch.Draw();
modelViewMatrix.PopMatrix(); // Erased the rotate
modelViewMatrix.Rotate(yRot * -2.0f, 0.0f, 1.0f, 0.0f);
modelViewMatrix.Translate(0.8f, 0.0f, 0.0f);
glBindTexture(GL_TEXTURE_2D, uiTextures[2]);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,
modelViewMatrix.GetMatrix(),
transformPipeline.GetProjectionMatrix(),
vLightTransformed,
vWhite,
0);
sphereBatch.Draw();
}
bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode)
{
GLbyte *pBits;
int nWidth, nHeight, nComponents;
GLenum eFormat;
// Read the texture bits
pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);
if(pBits == NULL)
return false;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB, nWidth, nHeight, 0,
eFormat, GL_UNSIGNED_BYTE, pBits);
free(pBits);
if(minFilter == GL_LINEAR_MIPMAP_LINEAR ||
minFilter == GL_LINEAR_MIPMAP_NEAREST ||
minFilter == GL_NEAREST_MIPMAP_LINEAR ||
minFilter == GL_NEAREST_MIPMAP_NEAREST)
glGenerateMipmap(GL_TEXTURE_2D);
return true;
}
//////////////////////////////////////////////////////////////////
// This function does any needed initialization on the rendering
// context.
void SetupRC()
{
// Make sure OpenGL entry points are set
glewInit();
// Initialze Shader Manager
shaderManager.InitializeStockShaders();
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// This makes a torus
gltMakeTorus(torusBatch, 0.4f, 0.15f, 40, 20);
// This makes a sphere
gltMakeSphere(sphereBatch, 0.1f, 26, 13);
// Make the solid ground
GLfloat texSize = 10.0f;
floorBatch.Begin(GL_TRIANGLE_FAN, 4, 1);
floorBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
floorBatch.Vertex3f(-20.0f, -0.41f, 20.0f);
floorBatch.MultiTexCoord2f(0, texSize, 0.0f);
floorBatch.Vertex3f(20.0f, -0.41f, 20.0f);
floorBatch.MultiTexCoord2f(0, texSize, texSize);
floorBatch.Vertex3f(20.0f, -0.41f, -20.0f);
floorBatch.MultiTexCoord2f(0, 0.0f, texSize);
floorBatch.Vertex3f(-20.0f, -0.41f, -20.0f);
floorBatch.End();
// Make 3 texture objects
glGenTextures(3, uiTextures);
// Load the Marble
glBindTexture(GL_TEXTURE_2D, uiTextures[0]);
LoadTGATexture("marble.tga", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_REPEAT);
// Load Mars
glBindTexture(GL_TEXTURE_2D, uiTextures[1]);
LoadTGATexture("marslike.tga", GL_LINEAR_MIPMAP_LINEAR,
GL_LINEAR, GL_CLAMP_TO_EDGE);
// Load Moon
glBindTexture(GL_TEXTURE_2D, uiTextures[2]);
LoadTGATexture("moonlike.tga", GL_LINEAR_MIPMAP_LINEAR,
GL_LINEAR, GL_CLAMP_TO_EDGE);
// Randomly place the spheres
for(int i = 0; i < NUM_SPHERES; i++) {
GLfloat x = ((GLfloat)((rand() % 400) - 200) * 0.1f);
GLfloat z = ((GLfloat)((rand() % 400) - 200) * 0.1f);
spheres[i].SetOrigin(x, 0.0f, z);
}
}
////////////////////////////////////////////////////////////////////////
// Do shutdown for the rendering context
void ShutdownRC(void)
{
glDeleteTextures(3, uiTextures);
}
// Called to draw scene
void RenderScene(void)
{
static CStopWatch rotTimer;
float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
modelViewMatrix.PushMatrix();
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
modelViewMatrix.MultMatrix(mCamera);
// Draw the world upside down
modelViewMatrix.PushMatrix();
modelViewMatrix.Scale(1.0f, -1.0f, 1.0f); // Flips the Y Axis
modelViewMatrix.Translate(0.0f, 0.8f, 0.0f); // Scootch the world down a bit...
glFrontFace(GL_CW);
DrawSongAndDance(yRot);
glFrontFace(GL_CCW);
modelViewMatrix.PopMatrix();
// Draw the solid ground
glEnable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, uiTextures[0]);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
static GLfloat vFloorColor[] = { 1.0f, 1.0f, 1.0f, 0.75f};
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_MODULATE,
transformPipeline.GetModelViewProjectionMatrix(),
vFloorColor,
0);
floorBatch.Draw();
glDisable(GL_BLEND);
DrawSongAndDance(yRot);
modelViewMatrix.PopMatrix();
// Do the buffer Swap
glutSwapBuffers();
// Do it again
glutPostRedisplay();
}
// Respond to arrow keys by moving the camera frame of reference
void SpecialKeys(int key, int x, int y)
{
float linear = 0.1f;
float angular = float(m3dDegToRad(5.0f));
if(key == GLUT_KEY_UP)
cameraFrame.MoveForward(linear);
if(key == GLUT_KEY_DOWN)
cameraFrame.MoveForward(-linear);
if(key == GLUT_KEY_LEFT)
cameraFrame.RotateWorld(angular, 0.0f, 1.0f, 0.0f);
if(key == GLUT_KEY_RIGHT)
cameraFrame.RotateWorld(-angular, 0.0f, 1.0f, 0.0f);
}
void ChangeSize(int nWidth, int nHeight)
{
glViewport(0, 0, nWidth, nHeight);
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
viewFrustum.SetPerspective(35.0f, float(nWidth)/float(nHeight), 1.0f, 100.0f);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
modelViewMatrix.LoadIdentity();
}
int main(int argc, char* argv[])
{
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(800,600);
glutCreateWindow("OpenGL SphereWorld");
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
glutSpecialFunc(SpecialKeys);
SetupRC();
glutMainLoop();
ShutdownRC();
return 0;
}