对场景的对象进行透视投影,然后再可见面上产生自然光照效果,可以实现场景的真实感显示。多说无益,看一个示例是最直接的方法(示例代码来源于http://blog.csdn.net/timidsmile/article/details/7017197)。
#include <gl/glut.h>
#define WIDTH 400
#define HEIGHT 400
static GLfloat angle = 0.0f;
void myDisplay(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(90.0f, 1.0f, 1.0f, 20.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0, 5.0, -10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
// 定义太阳光源,它是一种白色的光源
{
GLfloat sun_light_position[] = { 0.0f, 0.0f, 0.0f, 1.0f };
GLfloat sun_light_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
GLfloat sun_light_diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
GLfloat sun_light_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glLightfv(GL_LIGHT0, GL_POSITION, sun_light_position); //指定第0号光源的位置
glLightfv(GL_LIGHT0, GL_AMBIENT, sun_light_ambient); //GL_AMBIENT表示各种光线照射到该材质上,经过很多次反射后最终遗留在环境中的光线强度(颜色)
glLightfv(GL_LIGHT0, GL_DIFFUSE, sun_light_diffuse); //漫反射后~~
glLightfv(GL_LIGHT0, GL_SPECULAR, sun_light_specular);//镜面反射后~~~
glEnable(GL_LIGHT0); //使用第0号光照
glEnable(GL_LIGHTING); //在后面的渲染中使用光照
glEnable(GL_DEPTH_TEST); //这句是启用深度测试,这样,在后面的物体会被挡着,例如房子后面有棵树,如果不启用深度测试,你先画了房子再画树,树会覆盖房子的;但启用深度测试后无论你怎么画,树一定在房子后面(被房子挡着)
}
// 定义太阳的材质并绘制太阳
{
GLfloat sun_mat_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
GLfloat sun_mat_diffuse[] = { 0.0f, 0.0f, 0.0f, 1.0f };
GLfloat sun_mat_specular[] = { 0.0f, 0.0f, 0.0f, 1.0f };
GLfloat sun_mat_emission[] = { 0.5f, 0.0f, 0.0f, 1.0f };
GLfloat sun_mat_shininess = 0.0f;
glMaterialfv(GL_FRONT, GL_AMBIENT, sun_mat_ambient); //定义材料的前面采用 "多次反射"
glMaterialfv(GL_FRONT, GL_DIFFUSE, sun_mat_diffuse); //材料的前面为 漫反射
glMaterialfv(GL_FRONT, GL_SPECULAR, sun_mat_specular); //定义材料的前面为 镜面反射
glMaterialfv(GL_FRONT, GL_EMISSION, sun_mat_emission); //定义材料的前面为 镜面指数
glMaterialf(GL_FRONT, GL_SHININESS, sun_mat_shininess); //材料的前面 采用 的颜色
glutSolidSphere(2.0, 40, 32);
}
// 定义地球的材质并绘制地球
{
GLfloat earth_mat_ambient[] = { 0.0f, 0.0f, 0.5f, 1.0f };
GLfloat earth_mat_diffuse[] = { 0.0f, 0.0f, 0.5f, 1.0f };
GLfloat earth_mat_specular[] = { 0.0f, 0.0f, 1.0f, 1.0f };
GLfloat earth_mat_emission[] = { 0.0f, 0.0f, 0.0f, 1.0f };
GLfloat earth_mat_shininess = 30.0f;
glMaterialfv(GL_FRONT, GL_AMBIENT, earth_mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, earth_mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, earth_mat_specular);
glMaterialfv(GL_FRONT, GL_EMISSION, earth_mat_emission);
glMaterialf(GL_FRONT, GL_SHININESS, earth_mat_shininess);
glRotatef(angle, 0.0f, -1.0f, 0.0f);
glTranslatef(5.0f, 0.0f, 0.0f);
//如果使用glutSolidSphere函数来绘制球体,则该函数会自动的指定这些法线向量,
//不必再手工指出。如果是自己指定若干的顶点来绘制一个球体,则需要自己指定法线向量。
glutSolidSphere(2.0, 40, 32);
}
glutSwapBuffers(); //交换缓冲区
}
void myIdle(void)
{
angle += 1.0f;
if (angle >= 360.0f)
angle = 0.0f;
myDisplay();
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowPosition(200, 200);
glutInitWindowSize(WIDTH, HEIGHT);
glutCreateWindow("OpenGL光照演示");
glutDisplayFunc(&myDisplay);
glutIdleFunc(&myIdle);
glutMainLoop();
return 0;
}
这个示例模拟的是地球绕太阳旋转的场景,红色的太阳发射出白光照亮了蓝色的地球。我们来详解里面的函数。
太阳是一种点光源。我们使用glLightfv设置光源的各种属性,包括光源位置GL_POSITION,光源颜色(环境光GL_AMBIENT, 漫反射光GL_DIFFUSE, 镜面反射光GL_SPECULAR),辐射强度衰减系数等。
glLightfv(GL_LIGHT0, GL_POSITION, sun_light_position);//第一个参数为光源名,第二个参数指定属性,第三个参数为属性值
除了点光源外我们还可以设置方向光源(投射光源),即模拟台灯光照,中心光线最强,边缘光线强度最低。
glLightfv(GL_LIGHT0, GL_SPOT_POSITION, sun_light_position);//圆锥轴方向
glLightfv(GL_LIGHT0, GL_SPOT_CUTOFF, 30.0);//光源到圆锥轴的夹角被限制在30度的范围内
glLightfv(GL_LIGHT0, GL_SPOT_EXPONENT, 2.5);//从中心到边缘的衰减系数
可在全局级指定若干个OpenGL光照参数。
glLightModel*(paramName,paramValve);//两个参数分别指定参数名和参数值
光照效果除了和光源颜色有关外,也和对象表面反射特性密不可分。表面的反射系数和其他可选特性用下列函数来设定:
glMaterialf(surfFace,surfProperty,propertyValue);
surfFace赋予符号常量GL_FRONT,GL_BACK,GL_FRONT_AND_BACK之一,指定需要描述的面,surfProperty指定属性名(散射颜色gL_EMISSION、GL_SHININESS等)。
在应用光源前,需要激活光源并点亮光源。
glEnable(lightName);//点亮光源
glEnable(GL_LIGHTING);//激活光源
我们还可以使用雾气效果来模拟真实环境。
glEnable(GL_FOG);//激活雾气
glFogf(atmoParameter,paramValue);//设置雾气属性,例如颜色GL_FOG_COLOR,雾气衰减GL_FOG_DENSITY等等
为了更好地处理图元表面的明暗变化,可以指定相应的表面绘制方法来进行插值处理。
glShadeModel(surfRenderingMethod);
参数指定表面绘制方法,如果是GL_FLAT指定常数强度表面绘制方法,即不进行任何处理。GL_SMOOTH指定Gouraud表面绘制方法,在这种模式下,需要对每个平面指定一个法向量。
glBegin(GL_TRIANGLES):
glNormal3fv(normalVector1);
glVertex3fv(vertex1);
glNormal3fv(normalVector2);
glVertex3fv(vertex2);
glNormal3fv(normalVector3);
glVertex3fv(vertex3);
我们可以将法向量单位化来加速OpenGL效率
glEnable(GL_NORMALIZE);
和顶点数组。颜色数组一样,我们也可以指定一个法向量数组:
glEnableClientState(GL_NORMAL_ARRAY);
glNormalPointer(dataType,offset,normalArray);