绑定纹理到物体表面:
void drawPolygon(GLfloat a[3], GLfloat b[3], GLfloat c[3], GLfloat d[3])//根据四个点画一个面
{
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, Texture[0]);
glBegin(GL_POLYGON);
glTexCoord2f(0.0f, 0.0f);
glVertex3fv(a);
glTexCoord2f(1.0f, 0.0f);
glVertex3fv(b);
glTexCoord2f(1.0f, 1.0f);
glVertex3fv(c);
glTexCoord2f(0.0f, 1.0f);
glVertex3fv(d);
glEnd();
}
多重纹理就是在物体表面贴上多个纹理的方法,要使用多重纹理需要用到另一个库glext库。根据下面的代码就可以给物体表面贴上两种纹理:
PFNGLMULTITEXCOORD2FARBPROC glMultiTexCoord2fARB=NULL;
PFNGLACTIVETEXTUREARBPROC glActiveTextureARB=NULL;
bool canMultiTexture = true;
void multiTextureInit()//多重纹理的初始化
{
glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC)wglGetProcAddress("glActiveTextureARB");
glMultiTexCoord2fARB = (PFNGLMULTITEXCOORD2FARBPROC)wglGetProcAddress("glMultiTexCoord2fARB");
if(glActiveTextureARB == NULL)
canMultiTexture = false;
}
void multiTextureBegin()//多重纹理绑定
{
glEnable(GL_TEXTURE_2D);
glActiveTextureARB(GL_TEXTURE0_ARB);
glBindTexture(GL_TEXTURE_2D, Texture[0]);//纹理1
glActiveTextureARB(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, Texture[1]);//纹理2
}
值得注意的是,多重纹理对电脑设备有要求,主要是显示器要支持,所以一旦不支持,上面初始化的时候glActivetextureARB就会为null,这时候要做另外处理,不然后面使用这个为null的变量程序就会出错。
3)显示列表
显示列表是OpenGL提供的一种方便反复调用相同的显示函数的方法,比如你的程序中需要反复的描绘一个物体,你就最好用显示列表来调用,这样做能够大大优化性能。
调用显示列表是通过glCallList(列表索引)函数调用的,显然没一个显示列表都有一个对应的索引,通过这个索引去调用显示列表中的显示操作。下面的代码生成了一个画五角星的显示列表:
GLuint display_list;//一个五角星的显示列表索引
GLuint createDL()//创建一个五角星显示列表
{
GLuint DL;
DL = glGenLists(1);
glNewList(DL,GL_COMPILE);
drawFive();//画一个五角星
glEndList();
return DL;
}
当需要画一个五角星的时候调用glCallList(display_list);即可。
2 场景漫游
我实现的是模拟人在迷宫中走动寻找出口的情形,通过键盘的上下左右键控制视线的改变以及位置的移动。先理解一下gluLookAt函数,我的程序里参数是这样的gluLookAt(x, y, z, x + lx,y + ly,z + lz,0.0f,1.0f,0.0f) 总共有9个参数,前三个参数代表了照相机的位置,所以这里照相机的位置是(x,y,z),接下来三个参数是目标的中心位置,即(x+lx, y+ly,z+lz),后面三个参数一般设为0, 1, 0,表示的是照相机头部的方向,如果把照相机看错人眼,那照相机头部的方向也就是我们头的方向(所以一般向上)。因为要控制向前/后移动,所以需要知道此时视线的方向向量,实际上就是(lx, ly, lz),当改变视角是其实就是改变(lx, ly, lz)的值,所以当左右键事件发生时,进行以下计算:
void orientMe(float ang) //计算由于左右键盘操作而改变视点方向,使用左右方向键旋转照相机
{
lx = sin(ang);
lz = -cos(ang);
glLoadIdentity();
gluLookAt(x, y, z, x + lx,y + ly,z + lz, 0.0f,1.0f,0.0f);
}
可以注意到照相机位置还是不变的,因为只是改变了视线。当上下键事件发生时,改变的就是照相机的位置了。
void moveMeFlat(int direction) //计算视点由于上下键盘操作而移动的量,上下方向键使照相机沿视线前后移动
{
int prev_x = x, prev_z = z;
x = x + direction*(lx)*0.1;
z = z + direction*(lz)*0.1;
glLoadIdentity();
if(isWall[(int)(x + 93)][(int)(z + 93)])
{
x = prev_x;
z = prev_z;
}
gluLookAt(x, y, z, x + lx,y + ly,z + lz,0.0f,1.0f,0.0f);
}
3 粒子系统的实现
粒子系统不是什么具体的东西,而是是一个很好的编程设计思想,通常用来模拟雨、雪、雾、烟花等效果。粒子系统的实现主要问题就是如何设计粒子的行为以及如何渲染粒子以达到真实的效果。我的程序里粒子系统是最后加的,跟走迷宫没什么练习,只是觉得粒子系统挺神奇的,就试着实现各种五角星漫天飞扬的效果。这时粒子的类,定义了一个粒子的所有行为:
//次类是粒子类,实现粒子的一系列行为
#include<stdlib.h>
#include<GL\glut.h>
#include<time.h>
#define PI 3.1415
class particle
{
private:
GLfloat x;//位置x坐标
GLfloat y;//y坐标
GLfloat z;//z坐标
GLfloat v[3];//控制速度
GLfloat rotate[3];//控制旋转方向
GLfloat angle;//旋转的角度
GLfloat color[3];//五角星显示的颜色
GLuint display_list;//一个五角星的显示列表索引
public:
GLuint createDL()//创建一个五角星显示列表
{
GLuint DL;
DL = glGenLists(1);
glNewList(DL,GL_COMPILE);
drawFive();//画一个五角星
glEndList();
return DL;
}
void init()//随机初始化位置以及方向等信息
{
display_list = createDL();
angle = 0;
y = rand() % 40;
x = rand() % 181 - 90;
z = rand() % 181 - 90;
v[0] = (float)(rand() % 8) / (float)10 - 0.4;
v[1] = (float)(rand() % 8) / (float)10 - 0.4;
v[2] = (float)(rand() % 8) / (float)10 - 0.4;
rotate[0] = (float)(rand() % 7) / (float)7 + 5;
rotate[1] = (float)(rand() % 7) / (float)7 + 5;
rotate[2] = (float)(rand() % 7) / (float)7 + 5;
color[0] = (float)(rand() % 5) / (float)5 + 0.2;
color[1] = (float)(rand() % 5) / (float)5 + 0.2;
color[2] = (float)(rand() % 5) / (float)5 + 0.2;
}
void drawFive()//画五角星
{
GLfloat out_length = sqrt(1.0 / (2 - 2 * cos(72 * PI / 180))),
bx = out_length * cos(18 * PI / 180),
by = out_length * sin(18 * PI / 180),
cx = out_length * sin(36 * PI / 180),
cy = -out_length * cos(36 * PI / 180);
GLfloat fx = cx * (by - out_length) / (cy - out_length), fy = by,
in_length = sqrt(fx * fx + fy * fy),
gx = in_length * cos(18 * PI / 180),
gy = -in_length * sin(18 * PI / 180);
GLfloat point_a[2] = {0, out_length},
point_b[2] = {bx, by},
point_c[2] = {cx, cy},
point_d[2] = {-cx, cy},
point_e[2] = {-bx, by},
point_f[2] = {fx, fy},
point_g[2] = {gx, gy},
point_h[2] = {0, -in_length},
point_i[2] = {-gx, gy},
point_j[2] = {-fx, fy};
glBegin(GL_TRIANGLE_FAN);
glVertex2f(0.0f, 0.0f);
glVertex2f(point_a[0], point_a[1]);
glVertex2f(point_f[0], point_f[1]);
glVertex2f(point_b[0], point_b[1]);
glVertex2f(point_g[0], point_g[1]);
glVertex2f(point_c[0], point_c[1]);
glVertex2f(point_h[0], point_h[1]);
glVertex2f(point_d[0], point_d[1]);
glVertex2f(point_i[0], point_i[1]);
glVertex2f(point_e[0], point_e[1]);
glVertex2f(point_j[0], point_j[1]);
glVertex2f(point_a[0], point_a[1]);
glEnd();
}
void draw()//在(x, y, z)显示五角星
{
GLfloat diffuse[] = {color[0], color[1], color[2]};
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, diffuse);
glPushMatrix();
glTranslatef(x, y, z);
glRotatef(angle, rotate[0], rotate[1], rotate[2]);
glCallList(display_list);
glPopMatrix();
}
void move(float slowdown)//改变粒子位置及角度等信息
{
x += v[0] / slowdown;
y += v[1] / slowdown;
z += v[2] / slowdown;
angle += 10 / slowdown;
if(!(x >= -90 && x <= 90))
die();
else if(!(z >= -90 && z <= 90))
die();
else if(!(y >= 0 && y <= 50))
die();
}
void die()//粒子死亡,消失,重新初始化
{//可以加其他操作
init();
}
};