OpenGL紋理詳解與示例 C++ 版

OPENGL的紋理
在3D圖形中,紋理映射是廣泛使用的。紋理映射也是相當複雜的過程:
一 定義紋理
二 控制濾波
三 說明映射方式
四 繪製場景給出頂點的紋理座標和幾何座標
注意!!紋理映射只能在RGBA模式下使用,不適用於顏色索引模式

1.紋理定義
void glTexImage2D( GLenum target, GLint level, GLint components,
GLsizei width, GLsizei height, GLint border,
GLenum format, GLenum type, const GLvoid *pixels );
定義一個二維紋理映射。
target是常數 GL_TEXTURE_2D
level表示多級分辨率的紋理圖象的級數。若只有一種分辨率,level爲0。
components是從1到4的整數,1:選擇R;2:選擇R A;3:選擇R G B;
4:選擇R G B A;
width height是紋理的尺寸。
format和type描述映射格式和數據類型。它們與前面講的glDrawPixels()中
OPENGL的紋理
在3D圖形中,紋理映射是廣泛使用的。紋理映射也是相當複雜的過程:
一 定義紋理
二 控制濾波
三 說明映射方式
四 繪製場景給出頂點的紋理座標和幾何座標
注意!!紋理映射只能在RGBA模式下使用,不適用於顏色索引模式

1.紋理定義
void glTexImage2D( GLenum target, GLint level, GLint components,
GLsizei width, GLsizei height, GLint border,
GLenum format, GLenum type, const GLvoid *pixels );
定義一個二維紋理映射。
target是常數 GL_TEXTURE_2D
level表示多級分辨率的紋理圖象的級數。若只有一種分辨率,level爲0。
components是從1到4的整數,1:選擇R;2:選擇R A;3:選擇R G B;
4:選擇R G B A;
width height是紋理的尺寸。
format和type描述映射格式和數據類型。它們與前面講的glDrawPixels()中
GL_NEAREST_MIPMAP_NEAREST
GL_NEAREST_MIPMAP_LINEAR
GL_LINEAR_MIPMAP_NEAREST
GL_LINEAR_MIPMAP_LINEAR
2.1 濾波
原始紋理圖象是個方形圖象,把它映射到奇形怪狀的物體上,一般不可能圖象
上的一個象素對應屏幕的一個象素。因此局部放大縮小時,就要定義合適的濾
波方式(以2D爲例):
void glTexParameter(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
void glTexParameter(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
前者是放大濾波(GL_TEXTURE_MAG_FILTER),
後者是縮小濾波(GL_TEXTURE_MIN_FILTER);
另外,GL_NEAREST是利用最座標最靠近象素中心的紋理元素,這有可能使圖樣
走型,但計算速度快;GL_LINEAR利用線形插值,效果好但計算量大。

2.2重複與縮限
紋理映射可以重複映射或者縮限映射,重複映射時紋理可以在自己的座標S T方
向重複。
對於重複映射:
void glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
void glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
參數GL_REPEAT改爲GL_CLAMP,則縮限,所有大於1的紋理元素值置爲1。所有小於
0的紋理元素值置爲0。
0的紋理元素值置爲0。

3. 映射方式
處理紋理本身圖案顏色和物體本身顏色的關係:
void glTexEnv{if}[v](GLenum target,GLenum pname,TYPE param);
target必須是GL_TEXTURE_ENV;

pname是GL_TEXTURE_ENV_MODE,則param可以是 GL_DECAL GL_MODULATE或
GL_BLEND,說明紋理值與原來顏色不同的處理方式。
pname是GL_TEXTURE_ENV_COLOR,則參數param是包含4個浮點數(R、G、B、A)
的數組。這些值只在採用GL_BLEND紋理函數時才採用。

4. 紋理座標
座標的定義:紋理圖象是方形的,紋理座標可定義成s,t,r,q座標,仿照齊次
座標系的x,y,z,w座標。
void glTexCoord{1234}{sifd}[v](TYPE coords);
設置當前紋理座標,此後調用glVertex*()所產生的頂點都賦予當前的紋理座標。

5. 座標自動產生
有時不需要爲每個物體頂點賦予紋理座標,可以使用
void glTexGen{if}(GLenum coord,GLenum pname,TYPE param);
coord爲:GL_S GL_T GL_R或GL_Q,指明哪個座標自動產生
pname爲GL_TEXTURE_GEN_MODE時
param爲常數:GL_OBJECT_LINEAR GL_EYE_LINEAR或GL_SPHERE_MAP,它們決定用
哪個函數來產生紋理座標

pname爲GL_OBJECT_PLANE或GL_EYE_PLANE,param時一個指向參數數組的指針。

先請看一個簡單的例子:
////////////////////////////////////////////
//sample.cpp
#include "glos.h"
#include <GL/gl.h>
#include <GL/glaux.h>
#include "windows.h"
void myinit(void);
void CALLBACK display(void);
void CALLBACK reshape(GLsizei w,GLsizei h);

//創建紋理圖象的子程序
#define TEXTUREWIDTH 64
#define TEXTUREHEIGHT 64
GLubyte Texture[TEXTUREWIDTH][TEXTUREHEIGHT][3];
void makeTexture(void)
void makeTexture(void)
{
int i,j,r,g,b;
for(i=0;i<TEXTUREWIDTH;i++)
{
for(j=0;j<TEXTUREHEIGHT;j++)
{
r=(i*j)%255;
g=(4*i)%255;
b=(4*j)%255;
Texture[i][j][0 =(GLubyte)r;
Texture[i][j][1 =(GLubyte)g;
Texture[i][j][2 =(GLubyte)b;
}
}
}


void myinit(void)
{
auxInitDisplayMode(AUX_SINGLE|AUX_RGBA);
auxInitPosition(0,0,500,500);
auxInitWindow("sample1");
auxInitWindow("sample1");
glClearColor(0.0,0.0,0.0,0.0);
glClear(GL_COLOR_BUFFER_BIT);

//創建紋理圖象的原始數據保存在Texture[][][]中
makeTexture();
glPixelStorei(GL_UNPACK_ALIGNMENT,1);

//定義二維紋理
glTexImage2D(GL_TEXTURE_2D,0,3,TEXTUREWIDTH,
TEXTUREHEIGHT,0,GL_RGB,GL_UNSIGNED_BYTE,
&Texture[0][0][0]);
//控制濾波
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

//說明映射方式
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL);

//這個應該很熟了,啓用紋理模式
glEnable(GL_TEXTURE_2D);
glEnable(GL_TEXTURE_2D);
// glShadeModel(GL_FLAT);
}

void CALLBACK reshape(GLsizei w,GLsizei h)
{

glViewport(0,0,w,h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//定義立體視景體
gluPerspective(60.0,1.0*(GLfloat)w/(GLfloat)h,1.0,30.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0,0.0,-3.6);
}

void CALLBACK display(void)
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

glBegin(GL_QUADS);//繪製四邊形
//先繪製正方形,用來顯示實際未變形的紋理圖樣
//先繪製正方形,用來顯示實際未變形的紋理圖樣
glTexCoord2f(0.0,0.0);glVertex3f(-2.0,-1.0,0.0);
glTexCoord2f(0.0,1.0);glVertex3f(-2.0,1.0,0.0);
glTexCoord2f(1.0,1.0);glVertex3f(0.0,1.0,0.0);
glTexCoord2f(1.0,0.0);glVertex3f(0.0,-1.0,0.0);

//繪製一個不規則四邊形,用來顯示紋理是如何隨物體形狀而變形的。
glTexCoord2f(0.0,0.0);glVertex3f(0.0,-1.0,0.0);
glTexCoord2f(0.0,1.0);glVertex3f(0.0,1.0,0.0);
glTexCoord2f(1.0,1.0);glVertex3f(1.41421,1.0,-1.41421);
glTexCoord2f(1.0,0.0);glVertex3f(1.41421,-1.0,-1.41421);
glEnd();

glFlush();
}
void main(void)
{
myinit();

auxReshapeFunc(reshape);
auxMainLoop(display);
}
//end of sample
從例子來看,除了紋理的定義和控制比較麻煩和不容易理解外,其應用是十分
方便的。只須從紋理的座標系選出合適點附在實際物體頂點上即可。對於複雜
的紋理定義和控制,你也可以自行改變一些參數,看看效果如何。例如把
GL_LINEAR改成GL_NEAREST,則紋理的明顯變的粗糙,但計算結果卻快的多。
你也可以改動glTexCoord2f()的參數,看看如何選定紋理的一部分(例子中是
選定全部紋理)來貼圖。例如1.0改成0.5則選擇實際紋理的左上1/4部分來貼圖。
下次將給出一個更復雜的紋理應用的例子和說明。18:03 98-1-21

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


這次將結束紋理的內容。緊接上次,在上次平面紋理貼圖中,我們先
定義了一個數組(一維或二維...)來定義紋理的數據,所以紋理本身
是一個N維空間,有自己的座標和頂點。在上次的例子中,我們學會了
如何把紋理數據中的座標和屏幕物體座標相結合,就象把一塊布料扯成
合適的形狀貼在物體表面。而上次唯一沒有使用的函數是紋理座標的自
動產生(最後一個給出的函數),它的意義是產生一個環境紋理,所有
環境內的物體都賦予此紋理,很象一個特殊光源。
/////////////////////////////////////////////////////////////
//sample.cpp
#include "glos.h"
#include <GL/gl.h>
#include <GL/glaux.h>
#include "windows.h"
void myinit(void);
void CALLBACK display(void);
void CALLBACK reshape(GLsizei w,GLsizei h);

//定義一個一維紋理的數據,從生成來看,保持紅色、蘭色分量255(MAX),
//所以是漸變的紫色紋理,飽和度不斷變化。
//所以是漸變的紫色紋理,飽和度不斷變化。
#define TEXTUREWIDTH 64
GLubyte Texture[3*TEXTUREWIDTH];
void makeTexture(void)
{
int i;
for(i=0;i<TEXTUREWIDTH;i++)
{
Texture[3*i =255;
Texture[3*i+1 =255-2*i;
Texture[3*i+2 =255;
}
}
GLfloat sgenparams[]={1.0,1.0,1.0,0.0};

void myinit(void)
{
auxInitDisplayMode(AUX_SINGLE|AUX_RGBA);
auxInitPosition(0,0,500,500);
auxInitWindow("sample1");
glClearColor(0.0,0.0,0.0,0.0);
glClear(GL_COLOR_BUFFER_BIT);
//創建紋理
makeTexture();
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
//控制紋理
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
glTexParameterf(GL_TEXTURE_1D,GL_TEXTURE_WRAP_S,GL_REPEAT);
glTexParameterf(GL_TEXTURE_1D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameterf(GL_TEXTURE_1D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage1D(GL_TEXTURE_1D,0,3,TEXTUREWIDTH,0,
GL_RGB,GL_UNSIGNED_BYTE,Texture);
//唯一與前面例子不同的地方:啓用紋理座標自動產生,生成環境紋理
//紋理的方向S
glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR);
glTexGenfv(GL_S,GL_OBJECT_PLANE,sgenparams);
//啓用紋理
glEnable(GL_TEXTURE_1D);
glEnable(GL_TEXTURE_GEN_S);


//啓用消隱
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glDepthFunc(GL_LESS);

//一些繪圖控制,詳細可參閱VC5聯機幫助
glEnable(GL_CULL_FACE);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_AUTO_NORMAL);
glEnable(GL_NORMALIZE);
glFrontFace(GL_CW);
glCullFace(GL_BACK);
glMaterialf(GL_FRONT,GL_SHININESS,64.0);
// glShadeModel(GL_FLAT);
}

void CALLBACK reshape(GLsizei w,GLsizei h)
{

glViewport(0,0,w,h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

if(w<=h)
glOrtho(-4.0,4.0,-4.0*(GLfloat)h/(GLfloat)w,
glOrtho(-4.0,4.0,-4.0*(GLfloat)h/(GLfloat)w,
4.0*(GLfloat)h/(GLfloat)w,-4.0,4.0);
else
glOrtho(-4.0*(GLfloat)h/(GLfloat)w,
4.0*(GLfloat)h/(GLfloat)w,-4.0,4.0,-4.0,4.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}

void CALLBACK display(void)
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glRotatef(30.0,1.0,0.0,0.0);
//功能強大的輔助庫函數:呵呵畫出一個大茶壺。
auxSolidTeapot(1.5);
glPopMatrix();
glFlush();
}
void main(void)
{
myinit();
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glRotatef(30.0,1.0,0.0,0.0);
//功能強大的輔助庫函數:呵呵畫出一個大茶壺。
auxSolidTeapot(1.5);
glPopMatrix();
glFlush();
}
void main(void)
{
myinit();

auxReshapeFunc(reshape);
auxMainLoop(display);
}
//end of sample
////////////////////////////////////////////////////////////
至此紋理的全部內容已經完畢。從運行結果來看,一個物體全部進行
了表面的紋理映射。

-12---------------------------------------------

此次講解OPENGL複雜建模方式,將分幾個部分完成,這篇先介紹圖原擴展:
如何利用專用函數精確繪製平面圖形。下次會講解如何利用法向量生成曲面。
1.點和線
void glPointSize(GLfloat size);
設置點的寬度,size必須>0,缺省1

void glLineWidth(GLfoat width);
設置線寬,width>0,缺省爲1

void glLineStipple(GLint factor,GLushort pattern);
設置線的模式,factor用於對模式進行拉伸的比例因子,pattern是線的模式
例如11001100是虛線(1繪製,0不繪製)

必須要啓用glEnable(GL_LINE_STIPPLE)才能使用以上函數,不再使用時調用
glDisable(GL_LINE_STIPPLE)關閉,這與以前的glEnable();glDisable();的
用法都是類似的。請看下面例子:
///////////////////////////////////////////////////////////////////
//sample.cpp
#include "glos.h"
#include "glos.h"
#include <GL/gl.h>
#include <GL/glaux.h>
#include "windows.h"
void myinit(void);
void CALLBACK display(void);
void CALLBACK reshape(GLsizei w,GLsizei h);


void myinit(void)
{
auxInitDisplayMode(AUX_SINGLE|AUX_RGBA);
auxInitPosition(0,0,600,500);
auxInitWindow("sample1");
glClearColor(0.0,0.0,0.0,0.0);
glClear(GL_COLOR_BUFFER_BIT);

glShadeModel(GL_FLAT);
}
/*
void CALLBACK reshape(GLsizei w,GLsizei h)
{

glViewport(0,0,w,h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

if(w<=h)
glOrtho(-4.0,4.0,-4.0*(GLfloat)h/(GLfloat)w,
4.0*(GLfloat)h/(GLfloat)w,-4.0,4.0);
else
glOrtho(-4.0*(GLfloat)h/(GLfloat)w,
4.0*(GLfloat)h/(GLfloat)w,-4.0,4.0,-4.0,4.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
*/

//自定義的繪製直線的函數,參數爲起始點和終止點座標
void line2i(GLint x1,GLint y1,GLint x2,GLint y2)
{
glBegin(GL_LINES);
glVertex2f(x1,y1);
glVertex2f(x2,y2);
glEnd();
glEnd();
}

void CALLBACK display(void)
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

//首先繪製一系列點,點的大小不斷增加
int i;
glColor3f(0.8,0.6,0.4);
for(i=1;i<=10;i++)
{
glPointSize(i*2);
glBegin(GL_POINTS);
glVertex2f(30.0+((GLfloat)i*50.0),330.0);
glEnd();
}
//再繪製兩條虛線,第二條比第一條鬆散一些,由pattern參數即可看出
glEnable(GL_LINE_STIPPLE);
glLineStipple(1,0x0101);//間隔1位
glColor3f(1.0,0.0,0.0);
line2i(20,250,250,250);
glLineStipple(1,0x00ff);//間隔2位
glLineStipple(1,0x00ff);//間隔2位
glColor3f(0.0,0.0,1.0);
line2i(300,250,550,250);

//改變線的繪製寬度的效果--加寬
//重新畫出上面兩條虛線
glLineWidth(5.0);
glEnable(GL_LINE_STIPPLE);
glLineStipple(1,0x0101);
glColor3f(1.0,0.0,0.0);
line2i(50,150,250,150);
glLineStipple(1,0x00ff);
glColor3f(0.0,0.0,1.0);
line2i(300,150,550,150);

glFlush();
}
void main(void)
{
myinit();

// auxReshapeFunc(reshape);
auxMainLoop(display);
auxMainLoop(display);
}
//end of sample
//////////////////////////////////////////////

2.多邊形
void glPolygonMode(GLenum face,GLenum mode);
控制多邊形指定面的繪圖模式,
face爲:GL_FRONT GL_BACK或GL_FRONT_AND BACK
mode爲:GL_POINT GL_LINE或GL_FILL表示多邊型的輪廓點、輪廓線和填充模式
的繪製方式。缺省是填充方式。

void glPolygonStipple(const GLubyte *mask);
其中mask必須是指向32*32的位圖指針,1是繪製、0不繪製

使用上述函數也要調用:
glEnable(GL_POLYGON-STIPPLE);
glDisable(GL_POLYGON_STIPPLE);
請看下面例子:
/////////////////////////////////////////////
//sample.cpp
#include "glos.h"
#include <GL/gl.h>
#include <GL/gl.h>
#include <GL/glaux.h>
#include "windows.h"
void myinit(void);
void CALLBACK display(void);
void CALLBACK reshape(GLsizei w,GLsizei h);

//定義填充模式32*32點陣
GLubyte pattern[]={
0x00,0x01,0x80,0x00,
0x00,0x03,0xc0,0x00,
0x00,0x07,0xe0,0x00,
0x00,0x0f,0xf0,0x00,
0x00,0x1f,0xf8,0x00,
0x00,0x3f,0xfc,0x00,
0x00,0x7f,0xfe,0x00,
0x00,0xff,0xff,0x00,
0x01,0xff,0xff,0x80,
0x03,0xff,0xff,0xc0,
0x07,0xff,0xff,0xe0,
0x0f,0xff,0xff,0xf0,
0x1f,0xff,0xff,0xf8,
0x3f,0xff,0xff,0xfc,
0x3f,0xff,0xff,0xfc,
0x7f,0xff,0xff,0xfe,
0xff,0xff,0xff,0xff,

0xff,0xff,0xff,0xff,
0x7f,0xff,0xff,0xfe,
0x3f,0xff,0xff,0xfc,
0x1f,0xff,0xff,0xf8,
0x0f,0xff,0xff,0xf0,
0x07,0xff,0xff,0xe0,
0x03,0xff,0xff,0xc0,
0x01,0xff,0xff,0x80,
0x00,0xff,0xff,0x00,
0x00,0x7f,0xfe,0x00,
0x00,0x3f,0xfc,0x00,
0x00,0x1f,0xf8,0x00,
0x00,0x0f,0xf0,0x00,
0x00,0x07,0xe0,0x00,
0x00,0x03,0xc0,0x00,
0x00,0x01,0x80,0x00
};

void myinit(void)
void myinit(void)
{
auxInitDisplayMode(AUX_SINGLE|AUX_RGBA);
auxInitPosition(0,0,400,400);
auxInitWindow("sample1");
glClearColor(0.0,0.0,0.0,0.0);
glClear(GL_COLOR_BUFFER_BIT);

glShadeModel(GL_FLAT);
}
/*
void CALLBACK reshape(GLsizei w,GLsizei h)
{

glViewport(0,0,w,h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

if(w<=h)
glOrtho(-4.0,4.0,-4.0*(GLfloat)h/(GLfloat)w,
4.0*(GLfloat)h/(GLfloat)w,-4.0,4.0);
else
glOrtho(-4.0*(GLfloat)h/(GLfloat)w,
glOrtho(-4.0*(GLfloat)h/(GLfloat)w,
4.0*(GLfloat)h/(GLfloat)w,-4.0,4.0,-4.0,4.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
*/

void CALLBACK display(void)
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
//選用蘭色作爲填充色
glColor3f(0.0,0.0,1.0);
//啓用多邊形繪製模式
glEnable(GL_POLYGON_STIPPLE);
//利用定義好的填充模式繪製多邊形
glPolygonStipple(pattern);
//繪製長方形
glRectf(48.0,80.0,210.0,305.0);

glFlush();
}
void main(void)
{
//選用蘭色作爲填充色
glColor3f(0.0,0.0,1.0);
//啓用多邊形繪製模式
glEnable(GL_POLYGON_STIPPLE);
//利用定義好的填充模式繪製多邊形
glPolygonStipple(pattern);
//繪製長方形
glRectf(48.0,80.0,210.0,305.0);

glFlush();
}
void main(void)
{
myinit();

// auxReshapeFunc(reshape);
auxMainLoop(display);
}
//end of sample
例子中的運行結果是給出一個表面有定義圖樣的長方形

-13---------------------------------------------

這裏講解OPENGL的曲線生成
1.曲線定義
void glMap1{fd}(GLenum target,TYPE u1,TYPE u2,GLint stride,
GLint order,const TYPE *points);

target指出控制點的意義以及在points參數中需要多少值。具體如下:

target 意義
GL_MAP1_VERTEX_3 X Y Z頂點座標
GL_MAP1_VERTEX_4 X Y Z W頂點座標
GL_MAP1_INDEX 顏色索引
GL_MAP1_COLOR_4 R G B A
GL_MAP1_NORMAL 法向量
GL_MAP1_TEXTURE_COORD_1 S 紋理座標
GL_MAP1_TEXTURE_COORD_2 S T紋理座標
GL_MAP1_TEXTURE_COORD_3 S T R紋理座標

u1,u2是曲線變量U的範圍(具體可以參閱圖形學書籍)一般是0到1
stride是跨度,表示points中控制點偏移量(或說是控制點的維數)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章