1. 创建光源:
光源的几个特征: 颜色、位置、方向。
void glLightfv (GLenum light, // GL_LIGHT0 ~ GL_LIGHT7
GLenum pname,
const GLfloat *params); // pname指定了光源的属性值。
参数名 | 意义 |
---|---|
GL_AMBIENT | 光源的环境光亮度 |
GL_DIFFUSE | 光源的散射光亮度 |
GL_SPECULAR | 光源的镜面反射光亮度 |
GL_POSITION | 光源的位置 |
GL_SPOT_DIRECTION | 聚光方向 |
GL_SPOT_EXPONENT | 聚光指数 |
GL_SPOT_CUTOFF | 聚光终止角度 |
GL_CONSTANCE_ATENUATION | 恒定衰竭因子 |
GL_LINEAR_ATTENUATION | 线性衰减因子 |
GL_QUADRATIC_ATTENUATION | 二次衰减因子 |
颜色:允许为任何光源设置三个与颜色相关的参数---GL_AMBIENT、GL_DIFFUSE、GL_SPECULAR。
GL_DIFFUSE 的作用接近于直观想象中的“光的颜色”。 GL_SPECULAR一般与GL_DIFFUSE设置相同的颜色,这样符合真实世界的光照的规律。
定位和衰减:
// 设定光源的位置
GLfloat light_position[] = { 1.0, 1.0, 1.0, 1.0 };
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
如果设置light_position 的第四个参数设为0, 即为方向型光源, 它就像太阳一样,因为距离无穷远, 所以它产生的光线是平行的。
如果不为0, 称为位置型光源,台灯就是典型的一个例子。默认情况下, 位置光源360度发射光线。
对真实世界来讲, 光的强度会随着距离的增加而衰减。方向型光源位于无穷远处,其强度衰减对他没有任何意义。对于位置型光源, 需要设置衰减率。
opengl通过将光源产生的贡献乘以一个衰减系数使光的强度衰减:
衰减系数= 1/(kc + kld + kq* d*d);
d:光源的位置与顶点的距离, kc = GL_CONSTANT_ATTENUATION , kld = GL_LINEAR_ATTENUATION, kq = GL_QUADRATIC_ATTENUATION.
环境光、散射光、镜面反射光的贡献均是衰减的, 只有发射光和全局环境光不会发生衰减。
选择光照模型:
概念: 1. 全局环境光强度。
2. 观察点的位置是靠近场景还是位于无穷远处。
3. 对物体的正面和背面是否采取相同的光照计算。
4. 在执行纹理操作之后,是否将镜面颜色和环境颜色、散射颜色分离开来,并且应用它。
利用函数
glLightModelf (GLenum pname, GLfloat param);
glLightModelfv (GLenum pname, const GLfloat *params);
可指定光照模型的所有属性。
pname来定义所设置的光照模型的特征, param为设置了pname特征的光源指定的参数值。
参数名 | 默认值 | 意义 |
GL_LIGHT_MODEL_AMBIENT | (0.2, 0.2, 0.2) | 整个场景的环境RGBA强度 |
GL_LIGHT_MODEL_VIEWER | 0, GL_FALSE | 如何计算镜面反射角 |
GL_LIGHT_MODEL_TWO_SIDE | 0, GL_FALSE | 在单面光照和双面光照之间选择 |
GL_LIGHT_MODEL_COLOR_CONTROL | GL_SINGLE_COLOR | 镜面颜色的计算是否和环境颜色、散射颜色分离开来 |
全局环境光: 因为一个光源会给场景带来一定的环境光, 还可能存在于任何光源都无关的环境光。可以使用GL_LIGHT_MODEL_AMBIENT。
局部或无穷远点的视点: 一个特定顶点的镜面反射亮度,取决于该顶点的法线、顶点相对于光源的方向以及顶点相对于视点的方向。记住视点实际上并未因为调用了光照函数而移动;相反,光照计算是假设视点的位置移动了。
如何将视点设为局部视点: glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
双面光照: glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
可以通过glFrontFace()来控制opengl中哪个表面是正面。
分离镜面颜色: 在映射的光照计算中, 计算了光的环境成分、散射成份、镜面成分和发射成分,将它们简单的加在一起。默认情况下, 在光照处理后要施加纹理映射,这样镜面光可能会失去作用,或者说纹理映射看上去效果不怎么好。
可以如下调用: glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
定义材质属性:
场景中物体的材料属性: 环境颜色、散射颜色、镜面反射颜色、亮度和发射光的颜色。
大多数材料属性在概念上与创建光源时用到的特征类似。
glMaterialf (GLenum face, GLenum pname, GLfloat param);
glMaterialfv (GLenum face, GLenum pname, const GLfloat *params);
face 可以取: GL_FRONT、GL_BACK、GL_FRONT_AND_BACK。
材质属性由pname给出, 属性值由params给出。
参数名 | 默认值 | 意义 |
GL_AMBIENT | (0.2,0.2,0.2) | 材质的环境颜色 |
GL_DIFFUSE | (0.8,0.8,0.8,1.0) | 材质的散射颜色 |
GL_AMBIENT_AND_DIFFUSE | (0, 0, 0, 1) | 材质的背景颜色和散射颜色 |
GL_SPECULAR | (0, 0, 0, 1) | 材质的镜面反射颜色 |
GL_SHININESS | 0 | 镜面反射参数 |
GL_EMISSION | (0, 0, 0, 1) | 材质的放射光颜色 |
GL_COLOR_INDEXES | (0, 1, 1) | 背景颜色、散射颜色和镜面反射颜色的颜色索引 |
物体看起来是什么颜色,很大程度上受到散射光的影响。它取决于入射光中的散射光颜色和入射光相对于物体表面法线的角度。视点的位置对散射光没有影响。
环境光也影响整个物体的颜色。因为当光直射物体时, 散射光最强, 非直射时环境光的效果最明显。 环境光包括全局环境光和每一个光源产生的环境光。
对于真实世界的物体, 其散射光与环境光通常是同种颜色的。
GLfloat mat_amb_diff[] = { 0.1, 0.5, 0.8, 1.0 };
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_amb_diff);
镜面反射:物体的镜面反射会在物体的表面产生一个高亮区。观察者看到的镜面反射依赖于视点的位置-沿着反射光的方向亮度最高。
GL_SPECULAR 设置反射光材料的效果;GL_SHININESS设置高亮区域的大小和亮度,范围[0, 128.0]:数值越大,高亮区越小、亮度越高。
发射光颜色: GL_EMISSION设置RGB的颜色,常用这一功能来模拟场景中的灯光或其他各种光源。
颜色材料模式: 使改变材料性能的执行费用最小化的另一个技术是使用函数glColorMaterial().在任一时刻,只有一种模式处于激活状态。
其必须调用glEnable(GL_COLOR_MATERIAL), 然后可以根据绘图用glColor*函数来改变当前颜色、或调用glMaterial*()函数来改变其他材质属性。
当需要改变场景中大部分顶点的各个材质参数时, 应使用该函数。不用该函数的时候,应glDisable(GL_COLOR_MATERIAL)使其失效。
光照的数学计算
要真正掌握光照效果, 并得到参数值对顶点颜色的影响中较深层次的理解,需要进行大量的、长时间的编程实践和试验。
照亮一个顶点产生的颜色按下式来计算:
顶点颜色 = 该顶点发射光的颜色 + 材质环境光属性放大的全局环境光的颜色 + 从各个光源发出,经过衰减的环境光、散射光和镜面反射光颜色。
计算完成以后,颜色值被截取在【0,1】的范围之内。记住上面的颜色,对R,G,B各分量的计算是分别进行的。
材质的发射光:它就是分配给GL_EMISSION参数的RGB值。
放大的全局环境光: 由参数GL_LIGHT_MODEL_AMBIENT与材质的环境光属性(glMaterial*()分配的GL_AMBIENT值)相乘得到:
light_model(ambient) x material(ambient) .两个参数的相应的R,G,B值两两相乘得到。
光源的贡献:
整个光源的效果=衰减系数x聚光灯效果x(环境光项+散射光项+镜面反射光项)
衰减系数 = 1/(kc + kl*d + kq*d×d);
kc = GL_CONSTANT_ATTENUATION,
kl = GL_LINEAR_ATTENUTATION,
kq = GL_QUADRATIC_ATTENUATION。
d = 光源位置与顶点的距离。
聚光灯效果, 散射光项,镜面反射光项,具体参看 opengl权威指南。
一个例子:
#include <cstdlib>
#include <gl\glut.h>
namespace
{
GLfloat diffuseMaterial[4] = { 0.5, 0.5, 0.5, 1.0 };
}
void init()
{
glClearColor(0, 0, 0, 0);
glShadeModel(GL_SMOOTH);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuseMaterial);
GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMateriali(GL_FRONT, GL_SHININESS, 25.0);
GLfloat light_position[] = { 1.0, 1.0, 1.0, 1.0 };
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glColorMaterial(GL_FRONT, GL_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glutSolidSphere(1.0, 20, 16);
glFlush();
}
void reshape(int width, int height)
{
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (width < height)
{
glOrtho(-1.5, 1.5, -1.5*height / width, 1.5*height / width, -10, 10);
}
else
{
glOrtho(-1.5*height / width, 1.5*height / width, -1.5, 1.5, -10, 10);
}
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void mouse(int button, int state, int x, int y)
{
switch (button)
{
case GLUT_LEFT_BUTTON:
if (state == GLUT_DOWN)
{
diffuseMaterial[0] += 0.1;
if (diffuseMaterial[0] > 1.0)
{
diffuseMaterial[0] = 0;
}
glColor4fv(diffuseMaterial);
glutPostRedisplay();
}
break;
case GLUT_MIDDLE_BUTTON:
if (state == GLUT_DOWN)
{
diffuseMaterial[1] += 0.1;
if (diffuseMaterial[1] > 1.0)
{
diffuseMaterial[1] = 0;
}
glColor4fv(diffuseMaterial);
glutPostRedisplay();
}
break;
case GLUT_RIGHT_BUTTON :
if (state == GLUT_DOWN)
{
diffuseMaterial[2] += 0.1;
if (diffuseMaterial[2] > 1.0)
{
diffuseMaterial[2] = 0;
}
glColor4fv(diffuseMaterial);
glutPostRedisplay();
}
break;
default:
break;
}
}
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);
glutMouseFunc(mouse);
glutMainLoop();
return 0;
}