现实世界是多姿多彩的,为了能够使计算机模拟真实世界,达到逼真的效果,众多的科研人员也在不断努力,开发新的技术。针对个人计算机,IBM公司1981年开始提供单色显示卡MDA,能够显示黑白两种颜色,分辨率也只有720X350。同时提供的彩色显示卡CGA,也只能显示4种颜色,分辨率也只有640X200。1985年推出的EGA,能够支持16种颜色,分辨率达到640X350,此时已经有了比较简陋的图形程序,但和真实世界还相差甚远。直到后来VGA的出现,才改变了这一状况。VGA最高分辨率为640X480,在320X240的分辨率下能够提供256种颜色。此时,利用AutoDesk公司推出的3DS就已经能够制作出相当逼真的动画,而大名鼎鼎的DOOM也是工作在320X200X256模式下的。
随着PCI总线和AGP接口发展,显卡也越来越高档。3D显卡的内存动辄就达到128MB,颜色数目达到32位真彩色,分辨率也超过1600X1200,并且具有很强的加速功能。计算机创造出来的东西已经可以以假乱真。
在OpenGL中,对颜色的表征有两种模式:RGB/RGBA模式和颜色索引表模式。
在RGB模式下,所有的颜色值以R、G、B即红、绿、蓝三种颜色的值来表示。RGBA方式还需要增加第四个值Alpha,来表示透明度。颜色索引表模式实际上就是调色板模式,每一个表征颜色的值不是真正的颜色值,而是一个索引值,通过该索引指向颜色索引表中对应的以RGB表示的颜色值。256色的BMP文件就是典型的调色板方式。在大多数的OpenGL程序中都使用RGBA模式。
在OpenGL中,表示颜色的函数有32个。函数形式如下:
void glColor{[3][4]}{[b][d][f][i][s][ub][ui][us]}[v]
函数的后缀不同,则其中的参数取值也不同。但是OpenGL最终都会将其映射到0.0f~1.0f或-1.0f~1.0f的范围内。表3-1说明各函数具体不同。灵活运用不同的函数能够带来极大的方便,使用ui类型的函数即可表示32位真彩色。
表3-1 颜色函数说明
后缀 |
最小值 |
最大值 |
映射值范围 |
b |
-128 |
127 |
-1.0~1.0 |
d |
-1.7e308 |
1.7e308 |
-1.0~1.0 |
f |
-3.4e38 |
3.4e38 |
-1.0~1.0 |
i |
-2^31 |
2^31-1 |
-1.0~1.0 |
s |
-32768 |
32767 |
-1.0~1.0 |
ub |
0 |
255 |
0.0~1.0 |
ui |
0 |
2^32-1 |
0.0~1.0 |
us |
0 |
65535 |
0.0~1.0 |
在上一章的第一个OpenGL程序中,当窗口大小发生变化时,窗口中的图形并不随着变化,本来在整个窗口中的绘图就看不到了。因此,需要增加一个针对窗口大小变化进行处理的函数glSceneResize()。
在glSceneResize函数中,width和height是窗口当前的宽和高。由于设置透视矩阵时要计算宽高比,如果高度为0就会出问题,所以要针对这种情况进行调整。为了观察全部的场景,也需要对视口(Viewport)进行调整,使得窗口大小变化后窗口内的场景范围不变。
gluPerspective()用来设置透视投影矩阵,其原型如下:
void gluPerspective(
GLdouble fovy,
GLdouble aspect,
GLdouble zNear,
GLdouble zFar
);
其中fovy指Y轴方向的视角,单位是角度。如图3-1表示fovy是45度。
图3-1 透视投影角度fovy
aspect表示宽高比,zNear是近投影面的位置,其值大于或等于零;zFar表示远投影面的位置,其值小于或等于零。
void glSceneResize(int width, int height)
{
if (height==0)
{
height=1; //避免在后面的处理中作为除数的height为0
}
glViewport(0, 0, width, height); // 重置视口
//对投影矩阵进行设置
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// 计算窗口的外观比例
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
//对模型观察矩阵进行设置
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
然后在InitInstance添加对glSceneResize的调用。
BOOL InitInstance(int width, int height, int bits)
{
……
//添加对窗口的调整,以后还会用到。
glSceneResize(width, height);
return TRUE; // 成功
}
同时在窗口消息处理函数WndProc中加入对WM_SIZE消息的处理分支。
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_ACTIVATE:
……
//添加处理WM_SIZE消息的分支
case WM_SIZE:
{
glSceneResize(LOWORD(lParam),HIWORD(lParam));
return 0;
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
接下来我们来看最重要的部分glMain()。首先启动颜色缓冲和深度缓冲,加载单位矩阵用于后面的变换。然后调用平移函数glTranslatef将图形的绘图区域向左偏移20个单位,即屏幕的左上角,向屏幕里面即-Z方向平移50个单位,再绘制1000条线段。每一条线段的颜色随机使用256色中的一种颜色。为了使用随机函数rand(),需要在WinMain函数中调用随机种子生成函数srand(),以当前时间作为种子,可以保证程序每次运行时得到的随机数都不同。在设置线段的颜色中,我们使用了glColor3ub。
在屏幕的左下角,绘制三角形,屏幕的右上角绘制多边行,每一个三角形和多边行的颜色也是随机的。
在屏幕的右下角,绘制了一个正方形。这里分别对每一个顶点设置一种颜色,使用的颜色函数是glColor3us,这样表明取的是65536种颜色中随机的一种。OpenGL会将图形顶点的颜色值进行插值,来填充图形内部,所以当四边形的四个顶点颜色不同时就会在四边形内部产生混合的色彩效果。
void glMain()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity(); //加载单位矩阵
int i;
//进行坐标变换,将场景沿着X、Y、Z轴分别移动-10、-10、-20个单位。
glTranslatef(-20.0f, 0.0f,-50.0f);
for(i=0; i<1000; i++)
{
glColor3ub(rand()%256, rand()%256, rand()%256);
glBegin(GL_LINES); // 绘制线段
glVertex3s(rand()%20, rand()%20, 0); //起点
glVertex3s(rand()%20, rand()%20, 0); //终点
glEnd();
}
为了便于进行计算,加载单位矩阵后再进行平移,避免计算大量的相对平移结果。
glLoadIdentity();
glTranslatef(0.0f, 0.0f,-50.0f);
for(i=0; i<1000; i++)
{
glBegin(GL_POLYGON); // 绘制多边行,这里是5边形,每个顶点的颜色不同
glColor3ub(rand()%256, rand()%256, rand()%256);
glVertex3s(rand()%20, rand()%20, 0);
glColor3ub(rand()%256, rand()%256, rand()%256);
glVertex3s(rand()%20, rand()%20, 0);
glColor3ub(rand()%256, rand()%256, rand()%256);
glVertex3s(rand()%20, rand()%20, 0);
glColor3ub(rand()%256, rand()%256, rand()%256);
glVertex3s(rand()%20, rand()%20, 0);
glColor3ub(rand()%256, rand()%256, rand()%256);
glVertex3s(rand()%20, rand()%20, 0);
glEnd();
}
//变换矩阵初始化,进行绝对变换
glLoadIdentity();
glTranslatef(-20.0f,-20.0f,-50.0f);
for(i=0; i<1000; i++)
{
glColor3ub(rand()%256, rand()%256, rand()%256);
glBegin(GL_TRIANGLES); // 绘制三角形,三个角的颜色相同
glVertex3s(rand()%20, rand()%20, 0);
glVertex3s(rand()%20, rand()%20, 0);
glVertex3s(rand()%20, rand()%20, 0);
glEnd();
}
glLoadIdentity();
glTranslatef(1.0f, -1.1f,-5.3f);
glBegin(GL_QUADS); // 绘制一个四边形,每个顶点的颜色不同,产生混合效果。
glColor3us(rand()%65536, rand()%65536, rand()%65536);
glVertex3f(-1.0f, 1.0f, 0.0f);
glColor3us(rand()%65536, rand()%65536, rand()%65536);
glVertex3f(1.0f, 1.0f, 0.0f);
glColor3us(rand()%65536, rand()%65536, rand()%65536);
glVertex3f(1.0f, -1.0f,0.0f);
glColor3us(rand()%65536, rand()%65536, rand()%65536);
glVertex3f(-1.0f, -1.0f, 0.0f);
glEnd();
SwapBuffers(hDC);//交换前后缓冲区
}
图3-2
程序运行之后,可以看到屏幕被分为四部分,每一部分绘制着不同的图形。但是不管是线段还是多边形,看起来都还是二维图形,为了使之看起来有些立体感,我们对glMain()略加改动,使得每一部分的图形绕着自己的X、Y轴旋转。
修改后的glMain()如下:
void glMain()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity(); //加载单位矩阵
int i;
static int angle = 0; //旋转角度
//进行坐标变换,将场景沿着X、Y、Z轴分别移动-10、-10、-20个单位。
glTranslatef(-20.0f, 0.0f,-50.0f);
glRotated(angle, 1, 1, 0); //旋转仅针对X轴、Y轴,对Z轴无效
for(i=0; i<1000; i++)
{
glColor3ub(rand()%256, rand()%256, rand()%256);
glBegin(GL_LINES); // 绘制线段
glVertex3s(rand()%20, rand()%20, 0); //起点
glVertex3s(rand()%20, rand()%20, 0); //终点
glEnd();
}
//为了便于进行计算,加载单位矩阵后再进行平移,避免计算大量的相对平移结果。
glLoadIdentity();
glTranslatef(0.0f, 0.0f,-50.0f);
glRotated(angle, 1, 1, 0); //绕X轴、Y轴旋转
for(i=0; i<1000; i++)
{
glBegin(GL_POLYGON); // 绘制多边行,这里是5边形,每个顶点的颜色不同
glColor3ub(rand()%256, rand()%256, rand()%256);
glVertex3s(rand()%20, rand()%20, 0);
glColor3ub(rand()%256, rand()%256, rand()%256);
glVertex3s(rand()%20, rand()%20, 0);
glColor3ub(rand()%256, rand()%256, rand()%256);
glVertex3s(rand()%20, rand()%20, 0);
glColor3ub(rand()%256, rand()%256, rand()%256);
glVertex3s(rand()%20, rand()%20, 0);
glColor3ub(rand()%256, rand()%256, rand()%256);
glVertex3s(rand()%20, rand()%20, 0);
glEnd();
}
glLoadIdentity();
glTranslatef(-20.0f,-20.0f,-50.0f);
glRotated(angle, 1, 1, 0);
for(i=0; i<1000; i++)
{
glColor3ub(rand()%256, rand()%256, rand()%256);
glBegin(GL_TRIANGLES); // 绘制三角形,三个角的颜色相同
glVertex3s(rand()%20, rand()%20, 0);
glVertex3s(rand()%20, rand()%20, 0);
glVertex3s(rand()%20, rand()%20, 0);
glEnd();
}
glLoadIdentity();
glTranslatef(1.0f, -1.1f,-5.3f);
glRotated(angle, 1, 1, 0);
glBegin(GL_QUADS); // 绘制一个四边形,每个顶点的颜色不同,产生混合效果。
glColor3s(rand()%65536, rand()%65536, rand()%65536);
glVertex3f(-1.0f, 1.0f, 0.0f);
glColor3s(rand()%65536, rand()%65536, rand()%65536);
glVertex3f(1.0f, 1.0f, 0.0f);
glColor3s(rand()%65536, rand()%65536, rand()%65536);
glVertex3f(1.0f, -1.0f,0.0f);
glColor3s(rand()%65536, rand()%65536, rand()%65536);
glVertex3f(-1.0f, -1.0f, 0.0f);
glEnd();
//旋转角度累加
angle++;
SwapBuffers(hDC);//交换前后缓冲区
}
图3-3 三维平面