一、OpenGL光照模型
1、OpenGL假定光线是可以分解为红光、绿光、蓝光成分的;
2、人眼看到的物体颜色由环境中的光线和物体本身的材质属性共同决定;
3、光源的特征是由其发射的红、绿、蓝光的数量决定的;
4、材质的特征是由它向各个方向反射的红、绿、蓝入射光的百分比决定的;
5、OpenGL光照模型把光分为4种独立的成分:环境光、散射光、镜面光、发射光,4种成分都可以单独计算,并叠加在一起。
二、各种光的特点
* 环境光(ambient light):是那些在环境中进行了充分的散射,无法分辨其方向的光。
当环境光撞击表面时,它会向所有方向发散。
*散射光(diffuse light):来自某个方向,当它撞击表面时,它会均匀地向所有方向发散。
来自某个特定位置或者方向的任何光源很可能具有散射成分。
*镜面光(specular light):来自一个特定的方向,并且倾向于从表面向某个特定的方向反射。
*发射光(emissive color):模拟源自某个物体的光,不受任何光源的影响。
三、材质的颜色
*OpenGL中根据材质反射的红、绿、蓝光的比例来模拟它的颜色;
*材质也具有不同的环境、散射、镜面颜色,它们表示材质对于红、绿、蓝光的反射率;
*材质的环境反射属性将与每种入射光的环境光的成分组合,即散射反射属性与入射光的散射成分组合,镜面反射属性与入射光的镜面光成分组合.(这里的组合是相乘的意思)
四、光的RGB值与材质的RGB值的区别
*光线的颜色(RGB)与材质的RGB值存在本质的不同:
光线颜色成分的数量对应于每种颜色的完全强度的百分比;
材质的RGB值和它对这些颜色的反射比例相对应。
例如:
假设一束OpenGL光线的成分是(LR,LG, LB),一种材质具有的相应成分为(MR, MG, MB),那么若忽略其他所有的反射效果,进入眼睛的光就是( LR*MR, LG*MG, LB*MB);如果存在另一光源(LR1, LG1, LB1),则静茹人眼的光是( LR*MR + LR1*MR, LG*MG+LG1*MG, LB*MB+LB1*MB );如果相加结果大于1,会被截断到1。
五、顶点法向
*物体的法向决定它相对于光源的方向。OpenGL使用法向判断顶点从每个光源接收的光线数量。
*为了进行正确地光照计算,表面法线必须为单位长度,还必须保证对物体所进行的模型视图变换并没有对表面法线进行缩放,最终的法线仍然保持为单位长度。
*可以以GL_NORMALIZE或GL_RESCALE_NORMAL为参数调用glEnable()函数,来确保法线为单位长度。
注:每个光源都需要大量的计算,因此场景的渲染性能将受到场景中光源数量的影响。
六、创建光源
通过glLight*函数来设置光源的属性。
void glLight{if} ( GLenum light, GLenum pname, TYPE param );
void glLight{if}v ( GLenum light, GLenum pname, const TYPE *param);
七、光源位置与光强衰减
* GL_POSITION,表示光源位置,由四个值(X,Y,Z,W)表示。
若第四个分量为零,表示该光源位于无限远处,称之为方向性光源,可以认为所有的光线都是平行的,方向性光源的方向向量是:(-X, -Y, -Z);
若第四个分量不为零,则称之为位置性光源,X/W, Y/W, Z/W 表示光源的位置。
* 方向性光源由于不用计算衰减,所以计算时比位置性光源快了不少,因此在视觉效果允许的情况下,应该尽可能地使用方向性光源。
* 注意,由于方向性光源位于无限远处,因此不适合光强衰减的原则,所以只有位置性光源才需要考虑光强衰减。
*OpenGL把光源的强度乘以衰减因子,来计算空间某点的光强:
衰减因子 = 1/( kc + kl*d + kq* d^2)
其中:
d = 光源与顶点之间的距离
kc = GL_CONSTANT_ATTENUATION ,常量衰减因子
kl = GL_LINEAR_ATTENUATION, 线性衰减因子
kq = GL_QUADRATIC_ATTENUATION, 二次衰减因子
默认情况下,kc = 1.0, kl和kq都是0。
八、聚光灯
*对位置性光源的形状加以限制,使它的发射范围限于一个锥体之内,就可以实现聚光灯。
*GL_SPOT_CUTOFF 属性,有一个值,表示一个角度,它是光源发射光线所覆盖的角度的一半,其范围是[0.0, 90.0],此外还有180.0这个特殊值。默认情况下,聚光灯这个特性是被禁止的,GL_SPOT_CUTOFF的值是180.0,意味着光向所有方向发射。
*GL_SPOT_DIRECTION 属性,有三个值,表示一个向量,即光源的发射的方向。
*GL_SPOT_EXPONENT属性,有一个值,表示聚光的程度,即光的集中度。光的强度在光锥的中心大道最高,越靠近光锥的边缘,光的强度就越弱。
衰减的幅度是光线的方向与光线和它所照射的顶点的方向之间夹角的余弦值的聚光指数次方。
因此,聚光指数越高,光源的强度就越集中。
九、控制光源的位置和方向
*投影矩阵对光源的位置和方向没有影响。
可以通过修改模型视图矩阵的内容来操作光源的位置和方向。
* OpenGL中,当调用glLight*函数指定光源位置或方向时,这个光源的位置或方向将根据当前的模型视图矩阵进行变换,并以视觉坐标的形式存储。
*独立地移动光源,就像通过模型变换来旋转移动物体一样,很可能需要在display函数中进行,如:
glPushMatrix();
glRotated( 100, 1.0, 0.0, 0.0);
glLightfv( GL_LIGHT0, GL_POSITION, light_position);
glPopMatrix();
*使光源随观察点一起移动,需要在移动观察点(也就是视图变换)之前,设置好光源的位置,观察点的改变很可能要放在display函数中,而光源的设置要放在init()函数或reshape函数中。
光照练习代码:
#include "stdafx.h"
#include <windows.h>
#include "glew.h"
#include "glut.h"
#pragma comment(lib, "glew32d.lib")
#pragma comment(lib, "freeglut.lib")
void init( void )
{
GLfloat mat_specular[] = { 0, 0.0, 0, 1.0};
GLfloat mat_shininess[] = { 10.0};//影响高光点大小,0~128,值越大,高光点越小 //光泽度
GLfloat mat_ambient[] = { 1.0, 0.0, 0.0, 0.5 };//材质环境颜色
GLfloat mat_diffuse[] = { 0.0, 0.0, 0.0, 0.5};//材质散射颜色
GLfloat mat_amb_diff[] = { 0.0, 0.0, 0.0, 0.5};
GLfloat mat_emission[] = { 0.2, 0.2, 2.0, 0.0};
GLfloat light_position[] = { 0.0, 0.0, -6.0 ,1.0};
GLfloat light_ambient[] = { 1.0, 0.0, 0.0, 1.0};
GLfloat white_light[] = { 1.0, 1.0, 1.0, 1.0};
GLfloat red_light[] = { 1.0,0.0,0.0,1.0};
GLfloat blue_light[] = { 0.0,0.0,1.0,1.0};
GLfloat lmodel_ambient[] = { 0.1, 0.1, 0.1, 1.0};
GLfloat light1_ambient[] = { 0.2, 0.2, 0.2,1.0};
GLfloat light1_diffuse[] = { 1.0, 1.0, 1.0, 1.0};
GLfloat light1_specular[] = { 1.0, 1.0, 1.0, 1.0};
GLfloat light1_position[] = { -2.0, 2.0, 2.0, 1.0};
GLfloat light1_shininess[] = { 100.0 };
GLfloat spot_direction[] = { 1.0, -1.0, -1.0};
GLfloat light_model_ambient[] = { 1.0, 0.0, 0.0, 1.0}; //全局环境光
glClearColor( 0.0, 0.0, 0.0, 0.0);
glShadeModel( GL_SMOOTH );
glMaterialfv( GL_FRONT, GL_SPECULAR, mat_specular); //指定材质镜面光,对入射的各分量的
glMaterialfv( GL_FRONT, GL_SHININESS, mat_shininess);
glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient );
glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse );
glMaterialfv( GL_FRONT, GL_EMISSION, mat_emission );
glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_amb_diff );
glLightfv( GL_LIGHT0, GL_POSITION, light_position );
glLightfv( GL_LIGHT0, GL_DIFFUSE, white_light);
glLightfv( GL_LIGHT0, GL_SPECULAR, white_light);
glLightfv( GL_LIGHT0, GL_AMBIENT, light_ambient);
glLightModelfv( GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
glLightfv( GL_LIGHT1, GL_AMBIENT, light1_ambient );
glLightfv( GL_LIGHT1, GL_DIFFUSE, light1_diffuse );
glLightfv( GL_LIGHT1, GL_SPECULAR, light1_specular );
glLightfv( GL_LIGHT1, GL_POSITION, light1_position );
glLightf( GL_LIGHT1, GL_CONSTANT_ATTENUATION, 1.5);
glLightf( GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.1);
glLightf( GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 0.1);
glLightfv( GL_LIGHT1, GL_SHININESS, light1_shininess );
glLightf( GL_LIGHT1, GL_SPOT_CUTOFF, 45.0);
glLightfv( GL_LIGHT1, GL_SPOT_DIRECTION, spot_direction);
glLightf( GL_LIGHT1, GL_SPOT_EXPONENT, 2.0);
glLightModelfv( GL_LIGHT_MODEL_AMBIENT, light_model_ambient ); //设置全局环境光,默认为(0.2,0.2,0.2,1.0)
glLightModelf( GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE/*GL_TRUE*/ ); //设置是局部观察点还是无限远处的观察点
glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, TRUE );
glEnable( GL_LIGHTING );
glEnable( GL_LIGHT0 );
//glEnable( GL_LIGHT1);
glEnable( GL_DEPTH_TEST );
}
int nRotate= 10;
void display( void )
{
GLfloat no_mat[] = { 0.0, 0.0, 0.0, 1.0};
GLfloat mat_ambient[] = { 0.7, 0.7, 0.7, 1.0};
GLfloat mat_ambient_color[] = { 0.8, 0.8, 0.2, 1.0};
GLfloat mat_diffuse[] = { 0.1, 0.5, 0.8, 1.0};
GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0};
GLfloat no_shininess[] = {0.0};
GLfloat low_shininess[] = { 5.0};
GLfloat high_shininess[] = { 100.0};
GLfloat mat_emission[] = {0.3, 0.2, 0.2, 0.0};
GLfloat light_position[] = { 0.0, 1.0, -6.0 ,1.0};
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glPushMatrix();
glRotated( nRotate, 0.0, 1.0, 0.0);
glLightfv( GL_LIGHT1, GL_POSITION, light_position);
glMaterialfv( GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv( GL_FRONT, GL_DIFFUSE, mat_diffuse );
glMaterialfv( GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialf( GL_FRONT, GL_SHININESS, 50.0 );
glMaterialfv( GL_FRONT, GL_EMISSION, no_mat );
glLightfv( GL_LIGHT0, GL_POSITION, light_position);
glPopMatrix();
glutSolidSphere( 1.0, 1000, 80);
glFlush();
}
void reshape( int w, int h )
{
glViewport( 0, 0, (GLsizei)w, (GLsizei)h );
glMatrixMode( GL_PROJECTION );
glLoadIdentity( );
if( w <= h )
glOrtho( -5.5, 5.5, -5.5*(GLfloat)h/(GLfloat)w, 5.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
else
glOrtho( -5.5*(GLfloat)w/(GLfloat)h, -5.5*(GLfloat)w/(GLfloat)h, -5.5, 5.5, -10.0, 10.0);
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
}
void mouseMoveFunc( int x, int y)
{
static int i=0;
if( i%10 == 0)
{
nRotate +=15;
glutPostRedisplay();
}
++i;
}
int main( int argc, char** argv)
{
glutInit( &argc, argv);
glutInitDisplayMode( GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH );
glutInitWindowSize( 500, 500);
glutInitWindowPosition( 100, 100);
glutCreateWindow( argv[0]);
init();
glutDisplayFunc( display );
glutReshapeFunc( reshape );
glutMotionFunc( mouseMoveFunc);
glutMainLoop();
return 0;
}