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中控制点偏移量(或说是控制点的维数)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值