OpenGL-光照

1. 光照模型建立

OPenGL处理光照采用的是一种近似:
	把光照系统分为三部分:
		光源、材质和光照环境。

光源: 光的来源
材质:指接受光照的各种物体的表面,由于物体如何反射光线只有物体表面决定
		材质特点就决定了物体反射光线的特点。
光照环境:是指一些额外的参数,它们将影响最终的光照画面。
			比如一些光线经过多次反射后,已经无法分清它究竟是由哪个光源发出,这
					时指定一个环境亮度参数,可以使最后形成的画面更接近真实情况。

2. 法线向量

OpenGL中,法线方向是用一个向量表示。
为了实现光照效果,则需要在代码中为每个顶点指定其法线向量。

在指定法线向量时,只需要指定每一个顶点的法线向量,OpenGL会自行计算顶点
之间的其他点的法线向量,并且,法相向量一旦被指定,除非再指定新的法线向量
,否则以后指定的所有点都将以这一向量作为自己的法线向量。

glColor* 函数用于指定颜色
glNormal* 函数则可以指定法线向量

注意:使用glTranslate* 函数 或者glRotate* 函数可以改变物体的外观,但是法线向
 量并不会随之改变。
 	使用glScale* 函数,对每一坐标进行不同程度缩放,很有可能导致法线向量不
 	正确,所以应该尽量避免使用glScale* 函数,即使使用,最好保证各坐标等比例
 	缩放。

3. 控制光源

OpenGL中,支持有限数量光源。 至少会支持8个光源
GL_LIGHT0  0号光源
GL_LIGHT1  1号光源
...
GL_LIGHT7  7号光源


glEnable 函数用于开启它们。
例如: glEnable(GL_LIGHT0); 表示开启0号光源
glDisable 函数用于关闭光源。


每一个光源都可以设置其属性
	这一动作是通过glLight* 函数完成的
		该函数j具有三个参数
			1. 指明是哪一个光源的属性
			2. 指明设置该光源的哪一个属性
			3. 指明该属性的值设置为多少
			

(1)GL_AMBIENT、GL_DIFFUSE、GL_SPECULAR 属性表示了光源所发出的
		光的反射特性,每个属性由四个值表示,分别代表了颜色的RGBA值。
		GL_AMBIENT 表示该光源所发出的光,经过非常多次的反射后,最终
		遗留在整个光照环境中的强度。
		GL_DIFFUSE 表示该光源所发出的光,照射到粗糙表面时经过漫反射,所
		得到的光照强度。
		GL_SPECULAR 表示该光源所发出的光,照射到光滑表面时经过镜面反射,
		所得到的光的强度。
(2)GL_POSITION 属性。表示光源所在的位置。由四个值(X, Y, Z, W)表示。
		 如果第四个值W为零,则表示光源位于无限远处,前三个值表示了它所在的
		 方向。这种光源成为方向性光源。
		 如果第四个值W不为零,则X/W, Y/W, Z/W 表示了光源的位置。这种光源称为
		 位置性光源。对于位置性光源,设置其位置与设置多边形顶点的方式相似,
		 可以使用各种矩阵变换函数:glTranslate*,glRotate*
		 注意:方向性光源在计算时比位置性光源快了不少,因此在视觉效果允许的
		 情况下,应该尽可能的使用方向性光源。
(3)GL_SPOT_DIRECTION、GL_SPOT_EXPONENT、GL_SPOT_CUTOFF 属
         性。表示将光源作为聚光灯使用。只针对位置性光源有效。
         GL_SPOT_DIRECTION 属性有三个值,表示一个向量,即光源发射的方向。
         GL_SPOT_EXPONENT 属性只有一个值,表示聚光的程度。
         			为零表示光照范围内各个方向发射的光线强度相同。
         			为正数表示向中央集中,正对发射方向位置受更多光照,其他位置
         				受到较少光照,数值越大,聚光效果就越明显。
         GL_SPOT_CUTOFF 属性也只有一个值,表示一个角度,它是光源发射光线
         所覆盖角度的一半(如图),其取值范围在0 - 90之间	,也可以取 180 这个
         特殊值。取值为 180 时表示光源发射光线覆盖 360 度,即不使用聚光灯,向
         全周围发射。

OpenGL-光照_第1张图片

(4)GL_CONSTANT_ATTENUATION、GL_LINEAR_ATTENUATION、
			GL_QUADRATIC_ATTENUATION 属性。这三
			个属性表示了光源所发出的光线的直线传播特性。
			现实生活中 ,光线的强度随着距离的增加而减弱。
			OpenGL 把这个减弱的趋势抽象成函数:
				衰减因子 = 1 / (k1 + k2 * d + k3 * k3 * d)
				其中 d 表示距离,光线的初始强度乘以衰减因子,就得到对应距离的光线
				强度。
				k1, k2, k3 分别就是
				GL_CONSTANT_ATTENUATION, GL_LINEAR_ATTENUATION, 
				GL_QUADRATIC_ATTENUATION。
				通过设置这三个常
				数,就可以控制光线在传播过程中的减弱趋势。

4. 控制材质

材质则是通过 glMaterial*函数来设置的
glMaterial*函数有三个参数。
第一个参数表示指定哪一面的属性。
	可以是 GL_FRONT、GL_BACK 或者
	GL_FRONT_AND_BACK
第二、第三个参数与 glLight*函数的第二、三个参数作用类似。

(1)GL_AMBIENT、GL_DIFFUSE、GL_SPECULAR 属性。
			这三个属性与光源的三个对应属性类似,每一属性都由四
			个值组成。
			GL_AMBIENT 表示各种光线照射到该材质上,经过很多次反射后最终遗留
			在环境中的光线强度(颜色)。
			GL_DIFFUSE 表示光线照射到该材质上,经过漫反射后形成的光线强度
			(颜色)。
			GL_SPECULAR 表示光线照射到该材质上,经过镜面反射后形成的光线强
			度(颜色)。
			通常,GL_AMBIENT 和 GL_DIFFUSE 都取相同的值,可以达到比较真
			实的效果。
			使用 GL_AMBIENT_AND_DIFFUSE 可以同时设置 GL_AMBIENT 和 
			GL_DIFFUSE 属性。
(2)GL_SHININESS 属性。
		该属性只有一个值,称为“镜面指数”,取值范围是 0 到 128。该值越小,表示
		材质越粗糙,点光源发射的光线照射到上面,也可以产生较大的亮点。
		该值越大,表示材质越类似于镜面,光源照射到上面后,产生
		较小的亮点。
(3)GL_EMISSION 属性。
		该属性由四个值组成,表示一种颜色。
		OpenGL 认为该材质本身就微微的向外发射光线,以
		至于眼睛感觉到它有这样的颜色,但这光线又比较微弱,以至于不会影响到其
		它物体的颜色。
(4)GL_COLOR_INDEXES 属性。
		该属性仅在颜色索引模式下使用,由于颜色索引模式下的光照比 RGBA 模式要
		复杂,并且使用范围较小。

5. 选择关照模型

这里的光照模型就是前面提到的 光照环境。
光照模型包括四个部分:
	全局环境光线(即那些充分散射,无法分清究竟来自哪个光源的光线)的强度
	观察点位置是在较近位置还是在无限远处、
	物体正面与背面是否分别计算光照、
	镜面颜色(即 GL_SPECULAR 属性所指定的颜色)的计算是否从其它光照计算
	中分离出来,并在纹理操作以后在进行应用。

以上使用同一个函数glLightModel*来进行设置。有两个参数
第一个表示要设置的项目,
第二个参数表示要设置成的值。

GL_LIGHT_MODEL_AMBIENT 表示全局环境光线强度,由四个值组成。
GL_LIGHT_MODEL_LOCAL_VIEWER 表示是否在近处观看,若是则设置为 
GL_TRUE,否则(即在无限远处观看)设置为GL_FALSE。
GL_LIGHT_MODEL_TWO_SIDE 表示是否执行双面光照计算。如果设置为 
GL_TRUE,则 OpenGL 不仅将根据法线向量计算正面的光照,也会将法线向量反
转并计算背面的光照。
GL_LIGHT_MODEL_COLOR_CONTROL 表示颜色计算方式。如果设置为 
GL_SINGLE_COLOR,表示按通常顺序操作,先计算光照,再计算纹理。如果设置
为 GL_SEPARATE_SPECULAR_COLOR,表示将 GL_SPECULAR 属性分离出
来,先计算光照的其它部分,待纹理操作完成后再计算 GL_SPECULAR。后者通常
可以使画面效果更为逼真(当然,如果本身就没有执行任何纹理操作,这样的分离
就没有任何意义)。

6. 注意:

OpenGL 默认是关闭光照处理的。要打开光照处理功能,使用下面的语句:
glEnable(GL_LIGHTING);
要关闭光照处理功能,使用 glDisable(GL_LIGHTING);即可

示例代码

#include 
#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);

	// 定义太阳光源,它是一种白色的光源
	{	
		// GL_POSITION 属性的值 (x,y,z,w)  w为零表示无限远 x/w,y/w,z/w 表示光源位置
		GLfloat sun_light_position[] = {0.0f, 0.0f, 0.0f, 1.0f};
		// GL_AMBIENT 属性的值 R,G,B,A 
		GLfloat sun_light_ambient[] = {0.0f, 0.0f, 0.0f, 1.0f};
		// GL_DIFFUSE 属性的值 R,G,B,A 
		GLfloat sun_light_diffuse[] = {1.0f, 1.0f, 1.0f, 1.0f};
		// GL_SPECULAR 属性的值 R,G,B,A
		GLfloat sun_light_specular[] = {1.0f, 1.0f, 1.0f, 1.0f};

		// 设置 GL_POSITION (光源位置)属性 
		glLightfv(GL_LIGHT0, GL_POSITION, sun_light_position);
		// 设置 GL_AMBIENT( 光源发出的光,经过非常多次反射,遗留在整个光照环境中的强度)
		glLightfv(GL_LIGHT0, GL_AMBIENT, sun_light_ambient);
		// 设置 GL_DIFFUSE(光源发出的光,照射到粗糙表面时,经过漫反射,所得到的光强度)
		glLightfv(GL_LIGHT0, GL_DIFFUSE, sun_light_diffuse);
		// 设置 GL_SPECULAR(光源发出的光,照射到光滑表面时,经过镜面反射,所得到的光强度) 
		glLightfv(GL_LIGHT0, GL_SPECULAR, sun_light_specular);

		// 开启光源
		glEnable(GL_LIGHT0);
		// 打开光照处理功能
		glEnable(GL_LIGHTING);
		// 打开景深测试
		glEnable(GL_DEPTH_TEST);
	}
	// 定义太阳的材质并绘制太阳
	{
		/* GL_AMBIENT、GL_DIFFUSE、GL_SPECULAR 属性。这三个属性与光源的三个对应属性类似,每一属性都由四个值组成*/
		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};
		//  镜面指数 0 - 128 
		GLfloat sun_mat_shininess = 0.0f;
	
		// 设置 GL_AMBIENT (光线照射到该材质上,经过很多次反射后最终遗留在环境中的光线强度)
		glMaterialfv(GL_FRONT, GL_AMBIENT, sun_mat_ambient);
		// 设置 GL_DIFFUSE (光线照射到该材质上,经过漫反射后形成的光线强度)
		glMaterialfv(GL_FRONT, GL_DIFFUSE, sun_mat_diffuse);
		// 设置 GL_SPECULAR (光线照射到该材质上,经过镜面反射后形成的光线强度)
		glMaterialfv(GL_FRONT, GL_SPECULAR, sun_mat_specular);
		// 设置 GL_EMISSION (该属性由四个值组成,表示一种颜色。OpenGL 认为该材质本身就微微的向外发射光线,以至于眼睛感觉到它有这样的颜色,但这光线又比较微弱,以至于不会影响到其它物体的颜色)
		glMaterialfv(GL_FRONT, GL_EMISSION, sun_mat_emission);
		// 设置 GL_SHININESS  (该属性只有一个值,称为“镜面指数”,取值范围是 0 到 128。该值越小,表示材质越粗糙,点光源发射的光线照射到上面,也可以产生较大的亮点。该值越大,表示材质越类似于镜面,光源照射到上面后,产生较小的亮点。)
		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(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;
}
三维光照绘制总结: 
	1. 指定光源参数
	2. 指定材质参数
	3. 指定环境光参数
	4. 最后绘制实体

你可能感兴趣的:(opengl,OpenGL,光照)