光照模型
OpenGL光照模型分为三种光:Ambient Light 环境光, Diffuse Light漫反射光, Specular Light镜面反射光.
一、创建光源
void glLightfv (GLenum light, GLenum pname, const GLfloat *params)
light: OpenGL提供8个光源 GL_LIGHT0 ~ GL_LIGHT7
pname: 光源属性
GL_AMBIENT 环境光 默认 (0.0, 0.0, 0.0, 1.0)
GL_DIFFUSE 漫反射光 默认 (1.0, 1.0, 1.0, 1.0)
GL_SEPCULAR 镜面反射光 默认 (1.0, 1.0, 1.0, 1.0)
GL_POSITION 光源位置齐次坐标(x,y,z,w) 默认 (0.0, 0.0, 1.0, 0.0)
GL_SPOT_DIRECTION 点光源聚光方向矢量(x,y,z) 默认 (0.0, 0.0, -1.0)
GL_SPOT_EXPONENT 点光源聚光指数 默认 (0.0)
GL_SPOT_CUTOFF 点光源聚光截止角 默认 (180)
GL_CONSTANT_ATTENUATION 常量衰减因子 默认 (1.0)
GL_LINER_ATTENUATION 线性衰减因子 默认 (0.0)
GL_QUADRATIC_ATTENUATION 平方衰减因子 默认 (0.0)
params: 光源属性值
GLfloat light_position[] = { 0.0, 0.0, 0.0, 0.0 };
GLfloat light_ambient [] = { 0.0, 0.0, 0.0, 1.0 };
GLfloat light_diffuse [] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
glLightfv(GL_LIGHT0, GL_POSITION, light_position); // 指定位置
glLightfv(GL_LIGHT0, GL_AMBIENT , light_ambient ); // 设置环境光
glLightfv(GL_LIGHT0, GL_DIFFUSE , light_diffuse ); // 设置漫反射光
glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular); // 设置镜面反射光
光源的衰减
离光源越远则光强越弱。由于定向光源是模拟的无穷远得光源,所以不会根据距离改变而衰减,所以在定向光源中是禁用衰减的; 对于定位光源有衰减。OpenGL的光衰减是通过光源的发光量乘以衰减因子来实现衰减的。
衰减系数 = 1/(K0+K1*D+K2*D2)
其中: D = 光源位置与顶点之间的距离
K0 = GL_CONSTANT_ATTENUATION //常数衰减因子
K1 = GL_LINER_ATTENUATION // 线性衰减因子
K2 = GL_QUADRATIC_ATTENUATION // 二次衰减因子
OpenGL中默认的衰减因子为(1,0,0),即不进行衰减。
二、启用光源
glEnable(GL_LIGHTING);
glEnable(GL_LIGHTn);
三、材质
OpenGL用材质对光的红、绿、蓝三原色的反射率来近似定义材料的颜色。
物体的材质跟光源一样,也分为环境、漫反射和镜面反射成分,它们决定了材料对环境光、漫反射光和镜面反射光的反射程度。其中对环境光和漫反射光的反射程度决定了物体的颜色。
比如对于一朵红色的花,它对绿色和蓝色的反射能力比较弱,对红色分量的反射能力比较强,所以呈现出来是红色。材质的镜面反射率在RGB三个分量上通常是一致的,即反射的光跟光源的颜色基本是一致的,只不过强度会减弱。镜面反射光的强度还取决于观察点的位置,当观察点正好处于入射光的反射光线上,亮斑的亮度达到最大值。
这3个材质的属性都是反应的物体对外界光线的反射情况,有些物体本身可以发射弱光,OpenGL中是通过设置材料的辐射光来实现的,可以使物体看起来像是发射出设定的辐射光一样,以达到特殊的效果。物体的辐射光使用glMaterialfv(GL_FRONT, GL_EMISSION, earth_mat_emission)来设置。
总体来说材质定义了物体对环境光、漫反射光、镜面光的反射(吸收)能力。我们看到的物体的颜色(或亮度)是光源的颜色(或亮度)经过物体的材质反射(吸收)之后发散出来的颜色(或亮度)。
定义材质的函数使用glMaterialfv
void glMaterialfv (GLenum face, GLenum pname, const GLfloat *params);
face: 表示当前材质应用于哪个表面上 GL_FRONT、GL_BACK、GL_FRONT_AND_BACK
pname: 指定正在设置的材质属性
GL_AMBIENT 材质的环境光颜色 默认(0.2, 0.2, 0.2, 1.0)
GL_DIFFUSE 材质的漫反射光颜色 默认(0.8, 0.8, 0.8, 1.0)
GL_AMBIENT_AND_DIFFUSE 环境光和漫反射光颜色 默认(0.8, 0.8, 0.8, 1.0)
GL_SEPCULAR 材质镜面反射光颜色 默认(0.0, 0.0, 0.0, 1.0)
GL_SHININESS 镜面指数(光亮度) 默认(0.0)
GL_EMISSION 材质辐射光颜色 默认(0.0, 0.0, 0.0, 1.0)
GL_COLOR_INDEXS 材质环境、漫反射和镜面反射颜色 默认(0, 0, 1)
params: 属性值
GLfloat mat_ambient[] = { 0.3f, 0.3f, 0.3f, 1.0f };
GLfloat mat_diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
使用颜色材质:
除了glMaterialfv设置材质,还可以用glColorMaterial设置材质。glColorMaterial在绘图过程中根据glColor实时设置材质。
要使用glColorMaterial,需要启动颜色跟踪功能 glEnable(GL_COLOR_MATERIAL)
四、光照使用总结
1. 设置光源参数: 需要多次调用带有不同参数的glLightfv函数,设置环境光(Ambient Light)、漫射光(Diffuse Light)、镜面光(Specular Light)和光照位置(Position)等。
2. 开启光照,使用glEnable(GL_LIGHTING)和glEnable(GL_LIGHTXX)来开启光照和XX号光源,前者相当于光源的总开关,后者是对应的开启相应号数的光源。
3. glMaterialfv或glColorMaterial设置材质
4. 法线的设置
下图是对光照应用的例子,红色球代表太阳,材质的辐射光为红光,绿色球代表地球,绕太阳公转,材质的辐射光为蓝光,镜面反射光为红色(蓝色地球上的红色区域):
void CWinGLView::OnDraw(CDC* /*pDC*/)
{
// 创建透视效果视图
CRect rectSize;
GetClientRect(&rectSize);
double dbViewWidth = 800;
int cx = rectSize.Width();
int cy = rectSize.Height();
glViewport(0, 0, cx, cy);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(80.0f, 1.0f, 1.0f, 20.0f);
glClearColor(0.3, 0.7, 0.5, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清理颜色和深度缓存
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0, 12.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 }; //RGBA模式的环境光,为0
GLfloat sun_light_diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f }; //RGBA模式的漫反射光,全白光
GLfloat sun_light_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f }; //RGBA模式下的镜面光 ,全白光
glLightfv(GL_LIGHT0, GL_POSITION, sun_light_position);
glLightfv(GL_LIGHT0, GL_AMBIENT, sun_light_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, sun_light_diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, sun_light_specular);
//开启灯光
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
}
// 定义太阳的材质并绘制太阳
{
GLfloat sun_mat_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f }; //定义材质的环境光颜色,为0
GLfloat sun_mat_diffuse[] = { 0.0f, 0.0f, 0.0f, 1.0f }; //定义材质的漫反射光颜色,为0
GLfloat sun_mat_specular[] = { 0.0f, 0.0f, 0.0f, 1.0f }; //定义材质的镜面反射光颜色,为0
GLfloat sun_mat_emission[] = { 0.8f, 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(3.0, 40, 32);
}
// 定义地球的材质并绘制地球
{
GLfloat earth_mat_ambient[] = { 0.0f, 0.0f, 1.0f, 1.0f }; //定义材质的环境光颜色,骗蓝色
GLfloat earth_mat_diffuse[] = { 0.0f, 0.0f, 0.5f, 1.0f }; //定义材质的漫反射光颜色,偏蓝色
GLfloat earth_mat_specular[] = { 1.0f, 0.0f, 0.0f, 1.0f }; //定义材质的镜面反射光颜色,红色
GLfloat earth_mat_emission[] = { 0.0f, 0.0f, 0.0f, 1.0f }; //定义材质的辐射光颜色,为0
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(m_angle, 0.0f, -1.0f, 0.0f);
glTranslatef(7.0f, 0.0f, 0.0f);
glutSolidSphere(3.0, 40, 32);
}
glFlush();
SwapBuffers(m_hDC);
}