OpenGL学习日记-2014.12.21--光照

  o(╯□╰)o深患中度拖延症,也是从开始写这篇笔记到结束居然用了一个月。。。虽然中间是发生了不少事,不过明明就有无数机会可以完成,就是拖着没写代码,各种借口。。。面对如此拖延症该如何是好QAQ快哭了


正文:

突然觉得这些日记写着写着就没什么意思。。。只是简单梳理一下书中的内容,没经过很多的思考,可不写心里更虚,怕自己几天就把看的书忘了。对于很多概念,都由于没有好好去写代码验证,而理解流于表面。对于光照这章也是下决心细细琢磨一番(现在才下的决心o(╯□╰)o),毕竟这很重要。


一、光照和颜色密切相关,光照也是由不同颜色的光去描述,所以由于颜色的表示有两种模式:rgba模式和颜色索引模式,所以光照也分为rgba下的光照,和颜色索引模式下的光照。不过同样的是rgba模式下的光照灵活方便,主要还是使用rgba下的光照模式。

二、隐藏表面消除工具:
    本这提高渲染效率的目标,OpenGL最终应该只渲染看得的物体,而消除那些被其他物体挡住的物体,或者是它的一部分。在三维场景中,有些物体可能被遮挡,而随着观察点的变化,物体的遮挡关系也随之改变,所以必须维护更新这种遮挡关系。所谓的隐藏表面消除就是消除实心物体被其他物体所遮挡那部分。而实现他的最简单方法就是深度缓冲区(其实不是很明白书本在这里提一提深度缓冲区是几个意思。。)。
    深度缓冲区原理:把一个距离观察平面的(通常是近侧裁剪平面)的深度值(距离)与窗口中的每个像素关联。
                1.glClear( GL_DEPTH_BUFFER_BIT)可以把深度缓冲区的所有值设为最大值,最远距离。
                2.以任意顺序绘制物体
                3.硬件或软件所执行的图形计算会把每个被绘制的表面转换为窗口上的一些像素的集合,并不考虑它们之间的遮挡关系。如果OpenGL开启了深度测试,OpenGL则会计算这些表面与观察平面的距离,在绘制每个像素之前,把它的深度值与深度缓冲区的值比较,根据他么的大小关系去保留和丢弃对应的颜色值。

三、现实世界和OpenGL关照
    现实世界的光照是十分复杂的,完全的模式在计算量上近乎不可能。OpenGL只是进行了简单的模拟,并且大多数情况下效果还是不错的。
    在OpenGL光照模型中只有存在能吸引和反射光线的表面存在,光源才会产生效果。OpenGL光照模型分为4中独立的成分:环境光、散射光、镜面光、发射光。这四种成分可以进行单独的计算,并叠加在一起。
    a.环境光(ambient light):是那些在环境中进行了充分的散射,无法分辨其方向的光,它似乎来自所有方向(现实世界的定义)。
    b.散射光(diffuse light):它来自某一个方向,因此,当它从正面照射物体是物体显得亮一些,倾斜则显得暗一些。不过当它撞击表面时,他会均匀的向所有方向发散,所以无论眼睛在哪个位置,散射光看起来都一样亮。
    c.镜面光(specular light):也来自某个特定的方向,并且从表面向特定的方向反射。当一束经过充分校准的激光从一面高质量的镜子上反弹回来时,他所产生的几乎是100%的镜面反射光。具有光泽的金属或塑料而具有很高的镜面成分,粉笔和地毯则几乎不存在镜面成分(不得不说,红书的例子都挺贴切的)。这里提到镜面成分,是表面的材质,光照和材质相互作用产生了最后到达眼睛的光,看到的效果。
    d.发射(颜色)光(emissive color):他模拟那些源自某个物体的光,在OpenGL光照模型中发射颜色可以增加物体的强度,但是发射颜色不受任何光源的影响,并且不作为一个光参与其他物体的光照计算,也就是仅仅影响发射物体自己本身。

四、材料和颜色
    在OpenGL光照模型中,物体的颜色取决于他反射了红、蓝、绿光的成分比例决定。如果物体不反射光,那么它就是黑色的。每种 光都是由 不同比例的红、绿、蓝光组成你,对于材料而言,他的RGB值和它对光的这些颜色反射比例成 正比。如果一种材料的R=1、G=0.5、B=0,他将反射所有的红光,一半的绿光,不反射蓝光。假设,入射光线成分:(LR,LG,LB),一种材料Rgb(MR,MG,MB),在忽略了其他所有已反射效果之后,进入眼睛的光就是(LR·MR,LG·MG,LB·MB)。另外,如果有两束光进入眼睛,他们的成分分别是(R1,G1,B1)和(R2,G2,B2)那么OpenGL会把他们叠加(R1+R2,G1+G2,B1+B2),超过1的值将被截取为1.

 五、创建光源
    光源有几种属性:颜色,位置,方向。在OpenGL世界中,光源的处理思想大致和一般三维物体差不多。用于指定光源所有参数的函数:glLight*(GLenum light, GLenum pname, TYPE param/*param)系列函数。有些参数只作用于特定光源。

pname 参数名 缺省值 说明
GL_AMBIENT (0.0, 0.0, 0.0, 1.0) RGBA模式下环境光
GL_DIFFUSE (1.0, 1.0, 1.0, 1.0) RGBA模式下漫反射光
GL_SPECULAR (1.0,1.0,1.0,1.0) RGBA模式下镜面光
GL_POSITION (0.0,0.0,1.0,0.0) 光源位置齐次坐标(x,y,z,w)
GL_SPOT_DIRECTION (0.0,0.0,-1.0) 点光源聚光方向矢量(x,y,z)
GL_SPOT_EXPONENT 0.0 点光源聚光指数
GL_SPOT_CUTOFF 180.0 点光源聚光截止角
GL_CONSTANT_ATTENUATION 1.0 常数衰减因子
GL_LINER_ATTENUATION 0.0 线性衰减因子
GL_QUADRATIC_ATTENUATION 0.0 平方衰减因子
                                    表:参数名,默认值,提示
    1、颜色相关参数:GL_AMBIENT、GL_DIFFUSE、GL_SPECULAR。这三个参数可以与任何特定光源相关联。
            GL_AMBIENT:表示一个特定光源场景中添加的环境光RGBA强度。
            GL_DIFFUSE:表示特定光源在场景中添加的散射光(漫发射)RGBA强度。最接近于人类想象的光的颜色。GL_LIGHT0的GL_DIFFUSE的默认值是(1.0,1.0,1.0,1.0),其他GL_LIGHT1....GL_LIGHTn的默认颜色是(0.0,0.0,0.0,0.0)
            GL_SPECULAR:影响物体上的镜面亮点颜色。
        TIPS:以上颜色相关参数的alpha只在混合中产生作用。
    2、光的位置和光衰减:光源可以位于无限远,或者指定位置,靠近与场景。第一种称为方向性光源:当光线到达物体是,可以认为所有已光线都是平行的。方向性光源现实中的一个例子就是太阳。
第二种称为位置性光源:它在场景中的位置决定了它对场景的影响。台灯是位置性光源的一个例子。OpenGL中表示光源的位置坐标为:(x,y,z,w)如果最后一个值为0.0那么对应的光源就是方向性光源。反之如果w为非零值对应光源就是位置性光源。对于光的位置(x,y,z)虽然一些变换处理和一般物体差不多,但是要注意的是,(x,y,z)是相对于视角坐标系的坐标(以摄像机为原点建立的坐标系),这个后面有详细说明。
            在现实世界的光照会随着距离的增长而强度有所衰减,不过由于方向性光源处于无限远出,这个规则并不适合。对于位置性光源,OpenGL则进行了衰减计算:一衰减因子对光源实行帅经:衰减因子=1/(Kc+Kl*D+Kq*D*D) 其中:d=光源与定点之间的距离;Kc=GL_CONSTANT_ATTENUATION(距离衰减常量);Kl=GL_LINEAR_ATTENUATION(线性衰减常量);Kq=GL_QUADRATIC_ATTENUATION(二次方衰减常量)。说实话对于这三个常量有点不明觉厉,在默认情况下kc = 1.0, kl=kq=0,也可以而上述枚举值作为第二参数调用glLightf()对其进行赋值。光的衰减计算涉及到除法,因此对于程序的性能有一定的影响。
    3.聚光灯:聚光灯是对位置性光源进行限制之后产生的结果。一般位置性光源向所有方向发射光线,而聚光灯在向规定的圆锥方向发射光线。要对聚光灯进行定义需要两个参数: GL_SPOT_DIRECTIONGL_SPOT_CUTOFF,光轴和光锥边缘与光轴的夹角(书中图5-2)。GL_SPOT_CUTOFF的范围被限制在【0.0,90】之间,不过有个特殊性180这时候其实就是一般的位置性光源了。两个参数的设定同样采用glLight*()系列函数。值得注意的是,光轴的方向同样受变换影响,以视角坐标系存储。除了切角和光轴之外,还有一个可设定的参数:GL_SPOT_EXPONENT(光的集中度or光强分布),默认值为0,越靠近光锥的边缘,光强越弱,衰减幅度为光线方向与光线和它所照射顶点方向之间的夹角的余弦值的聚光指数的次方(撒巴黎哇嘎乃。。。),因此,聚光指数越高,光源强度越集中。
    4、多光源:至少可以指定8个光源(取决于OpenGL的具体实现)。
    5、控制光源的位置和方向(重头戏):
六、选择光照模型
    OpenGL的光照模型概念主要包括:全局环境光强度、观察点位于场景还是位于无限远处、物体的正面和背面是否执行不同的光照计算、镜面颜色是否应该从环境和散射颜色中分离出来,并在纹理操作之后再应用。glLightModel*()系列函数用于指定光照模型的所有属性:
参数名
默认值
含义
GL_LIGHT_MODEL_AMBIENT
(0.2,0.2,0.2,1.0)
整个场景的环境光强度
GL_LIGHT_MODEL_LOCAL_VIEWER
0.0 或 GL_FALSE
镜面反射角度是如何计算的
GL_LIGHT_MODEL_TWO_SIDE
0.0 或 GL_FALSE
指定了单面还是双面光照
GL_LIGHT_MODEL_COLOR_CONTROL
GL_SINGLE_COLOR
镜面颜色的计算是否从环境和散射颜色中分离出来

    1、全局环境光:每个光源都有可能为场景的环境光做出贡献。为了指定这种全局环境光的RGBA强度,可以使用GL_LIGHT_MODEL_AMBIENT作为参数调用glLightModel*().所以即使场景中么有任何散射光,我们还是可以看到场景中的物体。
    2、局部观察点或无限远的观察点(观察点就是指摄像机所在的位置,一般。。OpenGL默认观察点是无限远,所以这其实是个概念,也不就是摄像机所在位置。):观察点的位置影响镜面反射所产生的亮点的计算。具体的说,一个特定顶点上的亮点强度计算取决于这个顶点的法线,这个顶点和光源的方向以及这个顶点和观察点的距离。采用局部观察点,由于要计算每个顶点与观察的距离和方向所以对程序的性能有所影响。
    3、双面光照:所有的多边形都要执行光照计算,不管它是正面还是背面。但是,由于我们思维中所设定的光照条件一般适用于正面的多边形,因此背面的多变性光照计算一般是不正确的。多数情况下背面的多边形是看不见的,但有时需要看见的话,这是背面也应该执行正确的光照计算,以GL_LIGHT_MODEL_TWO_SIDE为参数调用glLightModel*()设置是否开启双面光照,这是OpenGL会把正面多边形的法线翻转,表示背面的多边形法线进行光照计算。
    4、镜面辅助颜色:对于典型的光照计算是环境、散射、镜面和发射成分的结果简单的叠加在一起。在默认情况下,纹理贴图是在光照指挥应用的。因此,镜面亮点会被削弱,纹理效果可能和预想的不一样。为了把镜面颜色应用推迟到纹理贴图之后,可以调用:glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR),,在这种模式下光照会为每个顶点产生两种颜色:一种是主颜色,由所有非镜面颜色组成。另一种是辅助颜色:他是所有镜面光颜色的总和。在进行过为例贴图的时候,只有主颜色与纹理进行组合。在纹理贴图之后,辅助颜色被添加到主颜色和纹理颜色的最终组合颜色。这样会产生更逼真的效果。
    5、启用光照:glEnable(GL_LIGHTING)
七、定义材料属性
    物体的材料属性包括:环境、散射、镜面颜色、光泽度?(镜面指数)、发射颜色,采用glMaterial*()系列函数进行设置: glMaterial*(GLenum face, GLenum pname, TYPE param)(还有向量版本) face:GL_FRONT、GL_BACK、GL_FRONT_AND_BACK
参数名
默认值
含义
GL_AMBIENT
(0.2,0.2,0.2,1.0)
材料的环境颜色
GL_DIFFUSE
(0.8,0.8,0.8,1.0)
材料的散射颜色
GL_AMBIENT_AND_DIFFUSE

材料的环境胡散射颜色
GL_SPECULAR
(0.0,0.0,0.0,1.0)
材料的镜面颜色
GL_SHININESS
0.0
镜面指数
GL_EMISSION
(0.0,0.0,0.0,1.0)
材料的发射颜色
GL_COLOR_INDEXES
0,1,1
环境,散射和镜面颜色索引(索引模式下)
注意:glMaterial*()设置的大多数属性都是颜色(RGBA ),但不管我们想其他参数提供的Alpha值是什么,所有顶点的Alpha值都是材料的散射颜色的Alpha值(GL_DIFFUSE).
   材料属性与光线相互作用产生效果:见:四:材料和颜色。剩下的函数调用不做记录。
Program:光照的位置和方向控制,深入理解,配合材料和颜色作用。

PS:光源的位置固然是以视角坐标系存储,如果只对摄像机进行变换,光源将跟随摄像机移动。而我们在对光源进行变换的时候依然站在世界坐标系的角度对光源进行变换,然后才由OpenGL转化为视角坐标系对应位置进行存储。
程序说明:程序使用了默认light0光源和light1自定义光源。light1为聚光灯,做了个旋转操作,对于聚光灯旋转的处理与一般模型旋转处理有一点不一样,就是旋转光源并没有正确改变光轴的方向,一开始并没有达到想要的效果,光源的照射表现的比较奇怪,至于如此表现原因,我还没仔细分析过。后来在以前的代码中找了个对向量进行旋转的代码,计算光轴旋转通过不断的改变光轴的方向达到灯塔的效果,顺带说一下,程序中的light1是个只发射红光的光源。第二方面就是材质变化,程序绘制了9个球体,其中第一排:从左到右改变的是聚光指数;第二排:从左到右改变的是散射颜色;第三排:从左到右改变的是发射颜色。
实际效果(网上随便找的一个gif生成软件,效果比较不理想):

源程序:
#include "light.h"
#include "CLVector.h"

//灯塔聚光灯,
//多球:材质,变化
//对光源位置,方向的操控
//动态更新材料属性
static GLfloat light1pos[] = { 0.0, 0.0, -5.0, 0.0 };

static GLfloat light1angle = 0.0;
static GLfloat camposx = 0;

//light2 
static GLfloat light2pos[] = { 0.0, 0.0, -5.0, 1.0 }; //方向性光源
static GLfloat light2ambient[] = { 0.0, 0.0, 0.0, 1.0 }; //环境光强度
static GLfloat light2diffuse[] = { 1.0, 0.0, 0.0, 1.0 }; //散射光强度
static GLfloat light2specular[] = { 0.0, 0.0, 0.0, 1.0 }; //镜面光光强度
static GLfloat light2dir[] = { 0.0, 0.0, 1.0 };

//材料属性
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[] = { 1.0, 0.8, 0.8, 1.0 };
GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat no_shininess[] = { 0.0 };
GLfloat low_shiiness[] = { 5.0 };
GLfloat high_shininess[] = { 100.0 };
GLfloat mat_emission[] = { 0.3, 0.2, 0.2, 0.0 };//发射颜色
GLfloat mat_emission_blue[] = { 0.0, 0.2, 1.0, 0.0 };//发射颜色

void lightInit()
{
	glLightfv(GL_LIGHT0, GL_POSITION, light1pos);
	
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0); 

	glLightfv(GL_LIGHT1, GL_POSITION, light2pos);
	glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, light2dir);
	glLightf(GL_LIGHT1, GL_SPOT_CUTOFF, 20.0f);
	glLightfv(GL_LIGHT1, GL_AMBIENT, light2ambient );
	glLightfv( GL_LIGHT1, GL_DIFFUSE, light2diffuse );
	glLightfv( GL_LIGHT1, GL_SPECULAR, light2specular );
	glEnable(GL_LIGHT1);
}

void lightUpdate(float dt)
{
	light1angle += 0.0001;
	//if (light1angle > 360.0f)
	//{
		//light1angle = 0.0f;
	//}
	static float dir = 0.001;
	if (camposx <= -10)
	{
		dir = 0.001;
	}
	if (camposx >= 10)
	{
		dir = -0.001;
	}
	camposx += dir;
	
}

void lightDiaplay()
{
	//位置和方向
	glPushMatrix();
		glLoadIdentity();
		gluLookAt(0.0, 0.0, -30.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
		//glRotatef(light1angle, 0.0, 1.0, 0.0);
		//gluLookAt(camposx, 0.0, -30.0, camposx, 0.0, 0.0, 0.0, 1.0, 0.0);//当我们尝试去平移摄像机(沿x轴),可以明显看到效果是光在球体上掠过,说明了光源跟随了摄像机移动。光源的位置是以摄像机坐标系存储的
		glPushMatrix();
			//glRotatef( light1angle, 0.0, 10000.0, 0.0 );//这样的旋转并没用改变光轴的方向,所以显得十分奇怪
			//glTranslatef(-5.0,0.0,0.0);
			CLVector vec(light2dir[0], light2dir[1], light2dir[2]);
			vec.Rotatef(0.001, 0.0, 1.0, 0.0);
			light2dir[0] = vec.x, light2dir[1] = vec.y, light2dir[2] = vec.z;
			glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, light2dir);//光轴旋转,实现灯塔效果
			glLightfv(GL_LIGHT1, GL_POSITION, light2pos);//如果这行单独打开,对光源的位置重新设定,由于光源在世界坐标系中位置不变,而我们使摄像机平移,这时球体的光照并没有任何改变。
			//glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, light1dir);
		glPopMatrix();
		glPushMatrix();
			GLUquadricObj* qobj = gluNewQuadric(); //二次曲面对象
			glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
			glMaterialfv( GL_FRONT, GL_DIFFUSE, mat_diffuse );
			glMaterialfv( GL_FRONT, GL_SPECULAR, no_mat );
			glMaterialfv( GL_FRONT, GL_SHININESS, no_mat );
			glMaterialfv( GL_FRONT, GL_EMISSION, no_mat );
			gluSphere(qobj, 2.0, 20, 16); //[2,2]
			
			glTranslatef( 0,8.0,0 );
			glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
			glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
			glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
			glMaterialfv(GL_FRONT, GL_SHININESS, low_shiiness);
			glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
			gluSphere(qobj, 2.0, 20, 16); //[1,2]

			glTranslatef(-10, 0.0, 0);
			glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
			glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
			glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
			glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess);
			glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
			gluSphere(qobj, 2.0, 20, 16);//[1,3]

			glTranslatef(20, 0.0, 0);
			glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
			glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
			glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
			glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess);
			glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
			gluSphere(qobj, 2.0, 20, 16);//[1,1]

			glTranslatef(0, -8.0, 0);
			glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
			glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
			glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
			glMaterialfv(GL_FRONT, GL_SHININESS, no_mat);
			glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
			gluSphere(qobj, 2.0, 20, 16);//[2,1]

			glTranslatef(-20, 0.0, 0);
			glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient_color);
			glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
			glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
			glMaterialfv(GL_FRONT, GL_SHININESS, no_mat);
			glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
			gluSphere(qobj, 2.0, 20, 16);//[2,3]

			glTranslatef(0, -8.0, 0);
			glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
			glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
			glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
			glMaterialfv(GL_FRONT, GL_SHININESS, no_mat);
			glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission_blue);
			gluSphere(qobj, 2.0, 20, 16);//[3,3]

			glTranslatef(10.0, 0.0, 0);
			glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
			glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
			glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
			glMaterialfv(GL_FRONT, GL_SHININESS, no_mat);
			glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission);
			gluSphere(qobj, 2.0, 20, 16);//[3,2]

			glTranslatef(10.0, 0.0, 0);
			glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
			glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
			glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
			glMaterialfv(GL_FRONT, GL_SHININESS, no_mat);
			glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
			gluSphere(qobj, 2.0, 20, 16);//[3,1]
		glPopMatrix();
		
	glPopMatrix();
}

猜你喜欢

转载自blog.csdn.net/coderling/article/details/42980319
今日推荐