首先介绍OpenGL的简单光照模型,它的反射光可以分为三个分量:环境反射光,漫反射光,镜面反射光。就是你要实现一个光照效果,需要设置这三个分量,三者之和达到你想要的光照效果。
1.环境光,简单来说就是环境中的光,似乎来自于各个方向
2.漫反射,来自一个方向,光它照射到物体上,则在各个方向上均匀地发散出去,不管视点在哪,物体都是一样亮。
3.镜面光,来自特定方向,并沿着另一方向反射出去。
glLightfv函数原型:void glLightfv(光源名, 类型, 赋值);
类型主要有四类:光源颜色---AMBIENT、DIFFUSE、SPECULAR和光源位置---POSITION
其中DIFFUSE的参数与光源颜色紧密,一般简单的光照模型只设置DIFFUSE就足够用了
GLfloat light0_diffuse[]= { 1.0, 0.0, 0, 1.0};
glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
下面是赋值的代码:
GLfloat light0_ambient[]= { 0.2, 0.2, 0.2, 1.0 };
GLfloat light0_diffuse[]= { 1.0, 0.0, 0, 1.0};
GLfloat light0_specular[] = { 1.0, 0, 0, 1.0 };
glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR,light0_specular);
设置光源位置
GLfloat light0_position[] = { 1.0, 1.0, 1.0, 0.0 };
glLightfv(GL_LIGHT0, GL_POSITION,light0_position);
开启光源
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);表示启动光照的总开关 glEnable(GL_LIGHT0);//打开0号光源
glEnable(GL_DEPTH_TEST);//打开深度缓冲区,绘制3D图像时候使用
完整代码
#include "stdafx.h"
#include <GL/glut.h>
#include "math.h"
void reshape(int w,int h)
{
glViewport(0,0,(GLsizei)w,(GLsizei)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(w<h)
glOrtho(-1.5, 1.5, -1.5,1.5 * (GLfloat)h / (GLfloat)w, -1.5,1.5);
else
glOrtho(-1.5* (GLfloat)w / (GLfloat)h, 1.5, -1.5,1.5, -1.5,1.5 );
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void init ()
{
glClearColor(1.0,1.0,1.0,0.0);
glColor3f(1.0,1.0,0.0);
GLfloat light0_ambient[]= { 0.2, 0.2, 0.2, 1.0 };
GLfloat light0_diffuse[]= { 1.0, 0.0, 0, 1.0};//红色点光源
GLfloat light0_specular[] = { 1.0, 0, 0, 1.0 };
GLfloat light0_position[] = { 1.0, 1.0, 1.0, 0.0 };
glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR,light0_specular);
glLightfv(GL_LIGHT0, GL_POSITION,light0_position);
glShadeModel(GL_SMOOTH);//光滑着色,过渡色效果
glEnable(GL_LIGHTING);//启动光照,总开关
glEnable(GL_LIGHT0);//打开0号光源
glEnable(GL_DEPTH_TEST);
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glutSolidTeapot(0.5);
glFlush();
}
int main(int argc, char* argv[])
{
glutInit(&argc,argv);
glutInitWindowPosition(200,200);
glutInitWindowSize(500,500);
glutCreateWindow("001");
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMainLoop();
return 0;
}
运行结果
我们发现茶壶的颜色被设置的光照给覆盖了,本身的颜色(黄色)并没有显示出来。这里引入一个新的函数---颜色材质函数glEnable(GL_COLOR_MATERIAL); 它确保了打开灯光效果之后还能保留图形颜色。使用时候放在init函数里即可。
添加后茶壶效果如下
事实上光源才是所有光照效果的基础,没有光源一切都无从谈起。OpenGL中我们可以设置8个光源,其编号分别为GL_LIGHT0、GL_LIGHT1、……、GL_LIGHT7。就相当于现实生活中有8个太阳,但每个太阳的位置、方向及它发出的光线可以完全不同。因此,OpenGL中我们设置光源时,主要就是设置其颜色、位置、方向等性质。另外,OpenGL中任何一个光源都可以发出不同强度的环境光、散射光、镜面反射光,这几个方面综合起来就决定了光源的颜色。
我们通过glLightfv(光源编号,光源特性,参数数据)来设置光源。其中,光源编号可取GL_LIGHT0、GL_LIGHT1、……、GL_LIGHT7共8个值。光源特性主要可取GL_AMBIENT(设置光源的环境光属性,默认值(0,0,0,1))、GL_DIFFUSE(设置光源的散射光属性,默认值(1,1,1,1))、GL_SPECULAR(设置光源的镜面反射光属性,默认值(1,1,1,1))、GL_POSITION(设置光源的位置,默认值(0,0,1,0))。参数数据格式要求为数组形式,即数学上的向量形式。
下面一段代码,我们将一个光源的性质进行完整设置:
GLfloat Va[]={0.4,0.4,0.4,1}; //光源环境光强度数组
GLfloat Vd[]={0.6,0.6,0.6,1}; //光源散射光强度数组
GLfloat Vs[]={0.6,0.6,0.6,1}; //光源镜面反射光强度数组
GLfloat Vp[]={1,1,1,1}; //光源位置数组
glLightfv(GL_LIGHT0,GL_AMBIENT,Va); //设置0号光源的环境光属性
glLightfv(GL_LIGHT0,GL_DIFFUSE,Vd); //设置0号光源的散射光属性
glLightfv(GL_LIGHT0,GL_SPECULAR,Vs); //设置0号光源的镜面反射光属性
glLightfv(GL_LIGHT0,GL_POSITION,Vp); //设置0号光源的位置属性
光源的性质设置完毕,还要调用glEnable(GL_LIGHT0)来打开光源。就相当于你买回手电筒、装好电池(设置光源),但如果不打开开关(打开电源),手电筒是不起作用的。
另外,对于GL_POSITION,其位置数组(x,y,z,w)定义了光源在空间中的位置。但三维空间为什么需要4个量呢?事实上这里采用的是齐次坐标,当w≠0时,它表示光源处于空间中(x,y,z)处,这时的光源称为定点光源;当w=0时,根据齐次坐标的性质,它表示光源位于无穷远处,此时光源称为定向光源,其所有光线几乎是相互平等的,如太阳。其光线方向由点(x,y,z)指向(0,0,0)。
将第2章中的正方体的例子做以下修改。修改后的void __fastcall TForm1::OpenGL1 GLPaint(TObject *Sender)处的代码如下(其余位置不变):
void __fastcall TForm1::OpenGL1GLPaint(TObject *Sender)
{
glEnable(GL_DEPTH_TEST); //隐藏表面消除
glClear(GL_DEPTH_BUFFER_BIT); //清除窗口颜色
const GLfloat glfLightAmbient1[4] = {0.1, 0.1, 0.1, 1.0};
const GLfloat glfLightAmbient2[4] = {0.4, 0.4, 0.4, 1.0};
const GLfloat glfLightDiffuse1[4] = {0, 0.8, 0.8, 1.0};
const GLfloat glfLightDiffuse2[4] = {0.8, 0.8, 0.8, 1.0};
const GLfloat glfLightSpecular1[4] = {0, 0.8, 0.8, 1.0};
const GLfloat glfLightSpecular2[4] = {0.8, 0.8, 0.8, 1.0};
const GLfloat glPosition1[4]={0,0,1,0};
const GLfloat glPosition2[4]={0.6,0.6,-0.6,1};
glLightfv(GL_LIGHT0, GL_AMBIENT, glfLightAmbient1);
glLightfv(GL_LIGHT0, GL_DIFFUSE, glfLightDiffuse1);
glLightfv(GL_LIGHT0, GL_SPECULAR, glfLightSpecular1);
glLightfv(GL_LIGHT0,GL_POSITION,glPosition1);
glLightfv(GL_LIGHT1, GL_AMBIENT, glfLightAmbient2);
glLightfv(GL_LIGHT1, GL_DIFFUSE, glfLightDiffuse2);
glLightfv(GL_LIGHT1, GL_SPECULAR, glfLightSpecular2);
glLightfv(GL_LIGHT1,GL_POSITION,glPosition2);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);//两面照亮
glEnable(GL_LIGHTING);//启用光照
glEnable(GL_LIGHT0);
glEnable(GL_LIGHT1);//打开光源
glEnable(GL_COLOR_MATERIAL);//启用颜色追踪
glColorMaterial(GL_FRONT,GL_AMBIENT_AND_DIFFUSE);
//物体正面的材料环境颜色和散射颜色,追踪glColor所设置的颜色(比较拗口,
事实上就是让OpenGL根据glColor所设置的颜色自动设置物体的反射强度)
glShadeModel(GL_FLAT);//设置颜色填充模式
glBegin(GL_QUAD_STRIP) ;
glVertex3f(i,-i,i);
glVertex3f(i,-i,-i);
glColor3f(1,0,0);
glVertex3f(-i,-i,i);
glVertex3f(-i,-i,-i);
glColor3f(0,1,0);
glVertex3f(-i,i,i);
glVertex3f(-i,i,-i);
glColor3f(0,0,1);
glVertex3f(i,i,i);
glVertex3f(i,i,-i);
glColor3f(1,1,0);
glVertex3f(i,-i,i);
glVertex3f(i,-i,-i);
glEnd();
glBegin(GL_QUADS);
glVertex3f(i,-i,i);
glVertex3f(-i,-i,i);
glVertex3f(-i,i,i);
glColor3f(1,0,1);
glVertex3f(i,i,i);
glVertex3f(i,-i,-i);
glVertex3f(-i,-i,-i);
glVertex3f(-i,i,-i);
glColor3f(0,1,1);
glVertex3f(i,i,-i);
glEnd();
glDisable(GL_LIGHTING);//关闭光照
glDisable(GL_LIGHT0);//关闭0号光源
glDisable(GL_LIGHT1); //关闭1号光源
}