OpenGL绘制光照和材质效果
本次任务主要实践三维空间的平移旋转、透视投影、光照及材质。有关阴影效果,课程的本节内容仅讲述了手动操作模型矩阵来绘制的阴影平面,而主流的阴影绘制方法是利用纹理和贴图来渲染阴影。
1. 将三个物体同轴排列
前面几个小节已经绘制了平面五角星、三维彩色立方体以及递归细分四面体法的三维球体,本节的要求首先是将三个物体同轴排列,使之在视景体中可见并设置透视投影来观察三者。为了更好地观察空间中的物体,本代码中额外绘制了三维坐标架,并绘制出了XOY平面,相关代码如下:
void frame()
{
// 绘制三维坐标架
glColor3f(0.3f, 0.3f, 0.3f);
glLineWidth(0.75);
glBegin(GL_LINES);
glVertex3f(-100.0f, 0.0f, 0.0f);
glVertex3f(100.0f, 00.0f, 0.0f);
glVertex3f(0.0f, -100.0f, 0.0f);
glVertex3f(0.0f, 100.0f, 0.0f);
glVertex3f(0.0f, 0.0f, -100.0f);
glVertex3f(0.0f, 0.0f, 100.0f);
glEnd();
// 绘制XOY平面
glColor4f(0.8f, 0.8f, 0.8f, 0.5f);
glNormal3f(0.0f, 0.0f, 1.0f);
glBegin(GL_QUADS);
glVertex3f(8.0f, 8.0f, 0.0f);
glVertex3f(-8.0f, 8.0f, 0.0f);
glVertex3f(-8.0f, -8.0f, 0.0f);
glVertex3f(8.0f, -8.0f, 0.0f);
glEnd();
}
绘制好坐标架后,三个物品依次排列时就可以清晰地看出其空间关系。三个物体绘制的具体代码不再赘述,以下是display函数中将三个物体依次排列的相关代码:
void display()
{
// 设置背景为白色
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 加载单位阵
glLoadIdentity();
// 设置相机的位置和视角
gluLookAt(7, 4, 4, 0.0, 0.0, 0.0, 0, 0, 1);
// 绘制坐标架以及XOY平面
frame();
// 平移坐标系,让三个待绘制物品的底部排列在X轴上
glTranslatef(0.0f, 0.0f, 1.0f);
// 在中心绘制三维彩色立方体,并绘制简单阴影
glTranslatef(-0.75f, -0.75f, -0.75f);
color_cube(1.5f);
cube_shadow(0.0f, 0.0f, 6.0f, 1.5f);
glTranslatef(0.75f, 0.75f, 0.75f);
// 在左侧(Y轴负半轴)绘制红色球面
glTranslatef(0.0f, -4.0f, 0.0f);
sphere();
glTranslatef(0.0f, 4.0f, 0.0f);
// 在右侧(Y轴正半轴)绘制平面的五角星
glTranslatef(0.0f, 4.0f, 0.0f);
pentagram();
glTranslatef(0.0f, -4.0f, 0.0f);
// 刷新帧缓存
glutSwapBuffers();
}
在不添加光照和阴影效果时,三个物品依次排列绘制出的效果如下:
可以看出,我们将三个物体的中心排列在同一高度,但由于使用了透视投影,靠右侧的五角星在视觉上要比靠左侧的球体更大,但实际上五角星的外接圆半径与球体半径是相同的,均为1。但由于没有使用光照效果,球体看上去只是一个圆(透视投影下被拉成椭圆)。
2. 光照、材质与简单阴影效果
光照和材质效果可以在init()函数中设置,此外在使用光照时,需要在绘制图像过程中增加每一点法向量的设置。指定法线向量的方式与指定颜色的方式有雷同之处。在指定颜色时,只需要指定每一个顶点的颜色,OpenGL就可以自行计算顶点之间的其它点的颜色。并且,颜色一旦被指定,除非再指定新的颜色,否则以后指定的所有顶点都将以这一向量作为自己的颜色。在指定法线向量时,只需要指定每一个顶点的法线向量,OpenGL会自行计算顶点之间的其它点的法线向量。并且,法线向量一旦被指定,除非再指定新的法线向量,否则以后指定的所有顶点都将以这一向量作为自己的法线向量。使用glColor*()函数可以指定颜色,而使用glNormal*()函数则可以指定法线向量。
在OpenGL中,仅仅支持有限数量的光源。使用GL_LIGHT0表示第0号光源,GL_LIGHT1表示第1号光源,依次类推。OpenGL至少会支持8个光源,即GL_LIGHT0到GL_LIGHT7。每一个光源都可以设置其属性,这一动作是通过glLight*()函数完成的。glLight*()函数具有三个参数,第一个参数指明是设置哪一个光源的属性,第二个参数指明是设置该光源的哪一个属性,第三个参数则是指明把该属性值设置成多少。GL_AMBIENT、GL_DIFFUSE、GL_SPECULAR这三个属性表示了光源所发出的光的反射特性(以及颜色),每个属性由四个值表示,分别代表了颜色的R, G, B, A值。GL_AMBIENT表示该光源所发出的光,经过非常多次的反射后,最终遗留在整个光照环境中的强度(颜色)。GL_DIFFUSE表示该光源所发出的光,照射到粗糙表面时经过漫反射,所得到的光的强度(颜色)GL_SPECULAR表示该光源所发出的光,照射到光滑表面时经过镜面反射,所得到的光的强度(颜色)。GL_POSITION属性表示光源所在的位置。由四个值(X, Y, Z, W)表示。如果第四个值W为零,则表示该光源位于无限远处,前三个值表示了它所在的方向。这种光源称为方向性光源,通常,太阳可以近似的被认为是方向性光源。如果第四个值W不为零,则X/W, Y/W, Z/W表示了光源的位置。这种光源称为位置性光源。对于位置性光源,设置其位置与设置多边形顶点的方式相似,各种矩阵变换函数例如:glTranslate*()、glRotate*()等在这里也同样有效。方向性光源在计算时比位置性光源快了不少,因此,在视觉效果允许的情况下,应该尽可能的使用方向性光源。
材质与光源相似,也需要设置众多的属性。不同的是,光源是通过glLight*()函数来设置的,而材质则是通过glMaterial*()函数来设置的。glMaterial*()函数有三个参数。第一个参数表示指定哪一面的属性。可以是GL_FRONT、GL_BACK或者GL_FRONT_AND_BACK。分别表示设置“正面”“背面”的材质,或者两面同时设置。第二、第三个参数与glLight*函数的第二、三个参数作用类似。GL_AMBIENT、GL_DIFFUSE、GL_SPECULAR这三个属性与光源的三个对应属性类似,每一属性都由四个值组成。GL_SHININESS属性只有一个值,称为“镜面指数”,取值范围是0到128。该值越小,表示材质越粗糙,点光源发射的光线照射到上面,也可以产生较大的亮点。该值越大,表示材质越类似于镜面,光源照射到上面后,产生较小的亮点。GL_EMISSION属性由四个值组成,表示一种颜色。OpenGL认为该材质本身就微微的向外发射光线,以至于眼睛感觉到它有这样的颜色,但这光线又比较微弱,以至于不会影响到其它物体的颜色。
void init()
{
// 计算五角星有关数据
get_pentagram();
// 设置逆时针排列的点围成的平面为正面
glFrontFace(GL_CCW);
// 设置不绘制背面,节省算力同时不会出现背面覆盖正面的情况
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
// 启用抗锯齿(使线平滑)
glEnable(GL_BLEND);
glEnable(GL_LINE_SMOOTH);
glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// 设置材质和光照的信息
// 有关光照与材质:https://blog.csdn.net/timidsmile/article/details/7017197
GLfloat mat_ambient[4] = { 1.0f, 1.0f, 1.0f, 0.0f };
GLfloat mat_diffuse[4] = { 1.0f, 1.0f, 1.0f, 0.0f };
GLfloat mat_specular[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
GLfloat mat_shininess[4] = { 100.0f };
GLfloat light_ambient[4] = { 0.2f, 0.2f, 0.2f, 0.0f };
GLfloat light_diffuse[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
GLfloat light_specular[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
GLfloat light_position[4] = { 0.0f, 6.0f, 0.0f, 1.0f };
// 设置正向面的材质和光源的光照
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
// 设置颜色材料,使光照模式下仍然可以显示原本的颜色
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
// 启用平滑着色功能
glShadeModel(GL_SMOOTH);
// 启用光照功能
glEnable(GL_LIGHTING);
// 启用0号光源
glEnable(GL_LIGHT0);
// 启用检测深度
glEnable(GL_DEPTH_TEST);
// 环境颜色设置为白色
glClearColor(1.0, 1.0, 1.0, 1.0);
}
可以看出,在(0, 0, 6)处设置一点光源,从上方照射三个物体,物体的侧面由于受到光照较少,会产生出阴影的质感。下面是变换光源位置以及光源三种属性后的不同效果。为了更好地展现光照效果,此处将环境颜色设置成深色。
从Z轴负向,即底部向上照射三个物体:
从顶部向下照射三个物体,但去除了环境光,只保留漫反射和镜面反射:
光照之后的任务要求即为绘制阴影。阴影是一种高级光照渲染,会为物体进一步增加立体感。阴影一般由深度贴图渲染实现,本节的课件中介绍了一种简单的绘制阴影的方法,即手动操作变换矩阵,将原物品直接映射到一个平面上。这种方法需要绘制原物品两次,并且需要手动计算阴影变换矩阵,不太灵活,代码耦合性较高且手动计算复杂。下面仅实现了立方体的阴影:
void cube_shadow(GLfloat x, GLfloat y, GLfloat z, GLfloat size)
{
GLfloat m[16] = { 0.0f };
m[0] = m[5] = m[10] = 1.0f;
m[11] = -1.0f / z;
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef(x, y, z);
glMultMatrixf(m);
glTranslatef(-x, -y, -z);
color_cube(size);
glPopMatrix();
}
添加阴影后效果如下,但可以看出阴影也是绘制出的多面体,同样也要受到全局光照的影响,其原本的颜色在光照下变亮,显然不符合阴影的效果。如果要修改,需要改动多面体本身的绘制代码,单独添加与光照有关的设置,非常麻烦,本代码中没有涉及,仅仅实践了简单的阴影绘制方法而已:
附录:完整代码
#include<GL/glut.h>
#include<math.h>
#include<iostream>
#define DEPTH 4
#define PI 3.1415926
using namespace std;
void frame()
{
// 绘制三维坐标架
glColor3f(0.3f, 0.3f, 0.3f);
glLineWidth(0.75);
glBegin(GL_LINES);
glVertex3f(-100.0f, 0.0f, 0.0f);
glVertex3f(100.0f, 00.0f, 0.0f);
glVertex3f(0.0f, -100.0f, 0.0f);
glVertex3f(0.0f, 100.0f, 0.0f);
glVertex3f(0.0f, 0.0f, -100.0f);
glVertex3f(0.0f, 0.0f, 100.0f);
glEnd();
// 绘制XOY平面
glColor4f(0.8f, 0.8f, 0.8f, 0.5f);
glNormal3f(0.0f, 0.0f, 1.0f);
glBegin(GL_QUADS);
glVertex3f(8.0f, 8.0f, 0.0f);
glVertex3f(-8.0f, 8.0f, 0.0f);
glVertex3f(-8.0f, -8.0f, 0.0f);
glVertex3f(8.0f, -8.0f, 0.0f);
glEnd();
}
void normalize(GLfloat* v)
{
GLfloat d = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
v[0] /= d; v[1] /= d; v[2] /= d;
}
void divide_triangle(GLfloat* a, GLfloat* b, GLfloat* c, int depth)
{
if (depth > 0) {
GLfloat ab[3], ac[3], bc[3];
for (unsigned int i = 0; i < 3; i++)
ab[i] = a[i] + b[i];
normalize(ab);
for (unsigned int i = 0; i < 3; i++)
ac[i] = a[i] + c[i];
normalize(ac);
for (unsigned int i = 0; i < 3; i++)
bc[i] = b[i] + c[i];
normalize(bc);
divide_triangle(a, ab, ac, depth - 1);
divide_triangle(b, bc, ab, depth - 1);
divide_triangle(c, ac, bc, depth - 1);
divide_triangle(ab, bc, ac, depth - 1);
}
else {
glBegin(GL_TRIANGLES);
glNormal3fv(a);
glVertex3fv(a);
glNormal3fv(b);
glVertex3fv(b);
glNormal3fv(c);
glVertex3fv(c);
glEnd();
}
}
void sphere()
{
GLfloat tetrahedron_vertex[][3] = {
0.0f, 0.0f, 1.0f,
0.0f, 0.942809f, -0.333333f,
-0.816497f, -0.471405f, -0.333333f,
0.816497f, -0.471405f, -0.333333f
};
glColor3f(1.00f, 0.00f, 0.00f);
divide_triangle(tetrahedron_vertex[0], tetrahedron_vertex[2], tetrahedron_vertex[1], DEPTH);
divide_triangle(tetrahedron_vertex[0], tetrahedron_vertex[3], tetrahedron_vertex[2], DEPTH);
divide_triangle(tetrahedron_vertex[0], tetrahedron_vertex[1], tetrahedron_vertex[3], DEPTH);
divide_triangle(tetrahedron_vertex[1], tetrahedron_vertex[2], tetrahedron_vertex[3], DEPTH);
}
GLfloat pentagram_vertex[5][3];
GLfloat pentagon_vertex[5][3];
void get_pentagram()
{
// 五角星看作在圆上内接的五边形的顶点连成的,此处设置外接圆半径[0, 1]
GLfloat r = 1.0f;
// 五角星可以在外接圆上旋转,此处设置旋转角度[0, 360)
GLfloat rotate = 18.0;
// 依次通过外接圆计算正五边形的五个顶点横纵坐标
GLfloat tmp[5][2];
for (int i = 0; i < 5; i++) {
tmp[i][0] = (GLfloat)(r * cos(((72.0 * (GLfloat)i + rotate) / 360.0) * (2 * PI)));
tmp[i][1] = (GLfloat)(r * sin(((72.0 * (GLfloat)i + rotate) / 360.0) * (2 * PI)));
}
// 将正五边形顺序的五个顶点对应到五角星顺序的五个顶点(0, 1, 2, 3, 4) -> (0, 2, 4, 1, 3)
for (int i = 0, j = 0; i < 5; i++, j = j + 2) {
int k = j % 5;
pentagram_vertex[i][1] = tmp[k][0];
pentagram_vertex[i][2] = tmp[k][1];
}
// 五角星的边相交得到中间的小五边形顶点,计算以便绘图时的着色
GLfloat x[4], y[4];
for (int i = 0; i < 5; i++) {
int v[4];
for (int j = 0; j < 4; j++)
v[j] = (i + j) % 5;
for (int j = 0; j < 4; j++)
x[j] = pentagram_vertex[v[j]][1], y[j] = pentagram_vertex[v[j]][2];
tmp[i][0] = ((x[2] - x[3]) * (x[1] * y[0] - x[0] * y[1])
- (x[0] - x[1]) * (x[3] * y[2] - x[2] * y[3])) /
((x[2] - x[3]) * (y[0] - y[1]) - (x[0] - x[1]) * (y[2] - y[3]));
tmp[i][1] = ((y[2] - y[3]) * (y[1] * x[0] - y[0] * x[1])
- (y[0] - y[1]) * (y[3] * x[2] - y[2] * x[3])) /
((y[2] - y[3]) * (x[0] - x[1]) - (y[0] - y[1]) * (x[2] - x[3]));
}
for (int i = 0, j = 0; i < 5; i++, j = j + 2) {
int k = j % 5;
pentagon_vertex[i][1] = tmp[k][0];
pentagon_vertex[i][2] = tmp[k][1];
}
for (unsigned int i = 0; i < 5; i++) {
pentagram_vertex[i][0] = 0.0f;
pentagon_vertex[i][0] = 0.0f;
}
}
void pentagram()
{
// 下面的段落绘制五角星中心所围成小正五边形的着色
glColor3f(1.0f, 1.0f, 0.0f);
glNormal3f(1.0f, 0.0f, 0.0f);
glBegin(GL_POLYGON);
for (unsigned int i = 0; i < 5; i++)
glVertex3fv(pentagon_vertex[4-i]);
glEnd();
// 下面的段落绘制五角星的线
glColor3f(0.0, 0.0, 0.0);
// 设置线的宽度(0, 10]
glLineWidth(2.5);
glBegin(GL_LINE_LOOP);
for (int i = 0; i < 5; i++)
glVertex3fv(pentagram_vertex[i]);
glEnd();
// 下面的段落绘制五角星的五个顶点(实心圆)
glColor3f(0.0, 0.0, 0.0);
// 设置实心圆的半径
GLfloat radius = 0.05f;
// 设置用来拟合圆形的多边形边个数
int sections = 50;
for (int i = 0; i < 5; i++) {
glBegin(GL_TRIANGLE_FAN);
glVertex3fv(pentagram_vertex[i]);
for (int j = 0; j <= sections; j++)
glVertex3f(0.0f, (GLfloat)(pentagram_vertex[i][1] + radius * cos(j * 2.0 * PI / sections)),
(GLfloat)(pentagram_vertex[i][2] + radius * sin(j * 2.0 * PI / sections)));
glEnd();
}
}
void color_cube(GLfloat size)
{
// 设置立方体的八个顶点坐标
static const GLfloat vertex[][3] = {
0.0f, 0.0f, 0.0f,
size, 0.0f, 0.0f,
0.0f, size, 0.0f,
size, size, 0.0f,
0.0f, 0.0f, size,
size, 0.0f, size,
0.0f, size, size,
size, size, size
};
// 设置绘制六个面时顶点的顺序
static const GLint index[][4] = {
0, 2, 3, 1,
0, 4, 6, 2,
0, 1, 5, 4,
4, 5, 7, 6,
1, 3, 7, 5,
2, 6, 7, 3
};
// 指定六个面的法向
static const GLfloat normal[][3] = {
0.0f, 0.0f, -1.0f,
-1.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f
};
// 绘制六个面
glBegin(GL_QUADS);
for (unsigned int i = 0; i < 6; i++) {
glNormal3fv(normal[i]);
for (unsigned int j = 0; j < 4; j++) {
// 每个顶点的RGB颜色值和其顶点位置坐标一致
glColor3f(vertex[index[i][j]][0] / size,
vertex[index[i][j]][1] / size, vertex[index[i][j]][2] / size);
glVertex3fv(vertex[index[i][j]]);
}
}
glEnd();
}
void cube_shadow(GLfloat x, GLfloat y, GLfloat z, GLfloat size)
{
GLfloat m[16] = { 0.0f };
m[0] = m[5] = m[10] = 1.0f;
m[11] = -1.0f / z;
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef(x, y, z);
glMultMatrixf(m);
glTranslatef(-x, -y, -z);
color_cube(size);
glPopMatrix();
}
void display()
{
// 设置背景为白色
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 加载单位阵
glLoadIdentity();
// 设置相机的位置和视角
gluLookAt(7, 4, 4, 0.0, 0.0, 0.0, 0, 0, 1);
// 绘制坐标架以及XOY平面
frame();
// 平移坐标系,让三个待绘制物品的底部排列在X轴上
glTranslatef(0.0f, 0.0f, 1.0f);
// 在中心绘制三维彩色立方体,并绘制简单阴影
glTranslatef(-1.0f, -1.0f, -1.0f);
color_cube(2.0f);
cube_shadow(0.0f, 0.0f, 10.0f, 1.5f);
glTranslatef(1.0f, 1.0f, 1.0f);
// 在左侧(Y轴负半轴)绘制红色球面
glTranslatef(0.0f, -4.0f, 0.0f);
sphere();
glTranslatef(0.0f, 4.0f, 0.0f);
// 在右侧(Y轴正半轴)绘制平面的五角星
glTranslatef(0.0f, 4.0f, 0.0f);
pentagram();
glTranslatef(0.0f, -4.0f, 0.0f);
// 刷新帧缓存
glutSwapBuffers();
}
// 窗口大小自适应函数,使得窗口大小改变时仍保持图形的比例不变
// 有关窗口自适应函数:http://blog.sina.com.cn/s/blog_5497dc110102w8qh.html
void reshape(int w, int h)
{
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0, (GLfloat)w / (GLfloat)h, 1.0, 20.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(7, 4, 4, 0.0, 0.0, 0.0, 0, 0, 1);
}
void init()
{
// 计算五角星有关数据
get_pentagram();
// 设置逆时针排列的点围成的平面为正面
glFrontFace(GL_CCW);
// 设置不绘制背面,节省算力同时不会出现背面覆盖正面的情况
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
// 启用抗锯齿(使线平滑)
glEnable(GL_BLEND);
glEnable(GL_LINE_SMOOTH);
glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// 设置材质和光照的信息
// 有关光照与材质:https://blog.csdn.net/timidsmile/article/details/7017197
GLfloat mat_ambient[4] = { 1.0f, 1.0f, 1.0f, 0.0f };
GLfloat mat_diffuse[4] = { 1.0f, 1.0f, 1.0f, 0.0f };
GLfloat mat_specular[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
GLfloat mat_shininess[4] = { 100.0f };
GLfloat light_ambient[4] = { 0.2f, 0.2f, 0.2f, 0.0f };
GLfloat light_diffuse[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
GLfloat light_specular[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
GLfloat light_position[4] = { 0.0f, 6.0f, 0.0f, 1.0f };
// 设置正向面的材质和光源的光照
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
// 设置颜色材料,使光照模式下仍然可以显示原本的颜色
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
// 启用平滑着色功能
glShadeModel(GL_SMOOTH);
// 启用光照功能
glEnable(GL_LIGHTING);
// 启用0号光源
glEnable(GL_LIGHT0);
// 启用检测深度
glEnable(GL_DEPTH_TEST);
// 环境颜色设置为白色
glClearColor(1.0, 1.0, 1.0, 1.0);
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
// 设置双缓冲和RGB颜色模式
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
// 设置窗口大小、位置和名称
glutInitWindowSize(1000, 500);
glutInitWindowPosition(100, 100);
glutCreateWindow("sphere");
init();
// 设置绘制函数、窗口大小自适应函数
glutDisplayFunc(display);
glutReshapeFunc(reshape);
// 进入主循环
glutMainLoop();
return 0;
}