OpenGL中的光照

    环境光:在环境中进行了充分的散射,无法分辨其方向的光。

    散射光:来自某个方向。

    镜面光:来自一个特定的方向,并且倾向于从表面某个特定的方向反射。

    除了以上三种光外,材料可能具有一种发射颜色,它模拟那些源自某个物体的光。


    为了实现明暗效果,必须启用光照计算,而且每种光源也必须被启用。对于单个光源,我们可以这样做:

    glEnable(GL_LIGHTING);

    glEnable(GL_LIGHT0)

    注:一旦光照被启用,glColor*()指定的颜色值将不再使用。


    指定法向量:

    物体的法线向量决定了它相对于光源的方向。表面法线必须为单位长度。

    void glNormal3<bsidf>(type x,type y,type z);

    void glNormal3<bsidf>(type* v)


光源的指定

    void glLight<if>(GLenum light,GLenum param,type value)

     void glLight<if>v(GLenum light,GLenum param,type* value)

    //为OpenGL光源light设置标量类型或向量类型的参数,即将参数param设为type


材质的指定

    void glMaterial<if>(GLenum face,GLenum name,type value)

     void glMaterial<if>v(GLenum face,GLenum name,type value)

    //为材质的某一面face设置标量或向量参数。参数name类型为type

    对于每个面,我们可以为其设置漫反射(GL_DIFFUSE)、镜面反射(GL_SPECULAR)以及环境反射(GL_AMBIENT)属性。通常漫反射和环境反射的属性是相同的,可以将两者一起设置。每个表面都可以向外辐射(GL_EMISSION).这一项不受光照计算的影响,所以无论光源如何,表面看起来都一样的。最后还有一个灰度系数(GL_SHININESS),该参数的值越大,材质的光泽度就越高。


旋转立方体的明暗计算

#include <gl/glut.h>
#include <math.h>
#include <iostream>

using namespace std;

int axis=0;
float theta[3];

GLfloat vertices[][3]={
	{-1.0,-1.0,1.0},
	{-1.0,1.0,1.0},
	{1.0,1.0,1.0},
	{1.0,-1.0,1.0},
	{-1.0,-1.0,-1.0},
	{-1.0,1.0,-1.0},
	{1.0,1.0,-1.0},
	{1.0,-1.0,-1.0}
};                              //定义立方体的8个顶点

GLint index[][4]={
	{0,3,2,1},
	{2,3,7,6},
	{3,0,4,7},
	{1,2,6,5},
	{4,5,6,7},
	{5,4,0,1}
};                              //定义每个面所需要那几个顶点


GLfloat normals[][3]={
	0.0,0.0,-1.0,
	0.0,1.0,0.0,
	-1.0,0.0,0.0,
	1.0,0.0,0.0,
	0.0,0.0,1.0,
	0.0,-1.0,0.0
};                             //定义每个面的法向量

typedef struct  lightingStruct 
{
	GLfloat ambient[4];
	GLfloat diffuse[4];
	GLfloat specular[4];
}lightingStruct;

lightingStruct whiteLighting={
	{0.0,0.0,0.0,1.0},
	{1.0,1.0,1.0,1.0},
	{1.0,1.0,1.0,1.0}
};

lightingStruct coloredLighting={
	{0.2,0.0,0.0,1.0},
	{0.0,1.0,0.0,1.0},
	{0.0,0.0,1.0,1.0}
};

typedef struct materialStruct{
	GLfloat ambient[4];
	GLfloat diffuse[4];
	GLfloat specular[4];
	GLfloat shininess;
}materialStruct;

materialStruct brassMaterials={
	{0.33,0.22,0.03,1.0},
	{0.78,0.57,0.11,1.0},
	{0.99,0.91,0.81,1.0},
	27.8
};

materialStruct redplasticMaterials={
	{0.3,0.0,0.0,1.0},
	{0.6,0.0,0.0,1.0},
	{0.8,0.6,0.6,1.0},
	32.0
};

materialStruct whiteShinyMaterials={
	{1.0,1.0,1.0,1.0},
	{1.0,1.0,1.0,1.0},
	{1.0,1.0,1.0,1.0},
	100.0
};

GLfloat light_0pos[4]={0.9,0.9,2.25,0.0};

materialStruct* currentMaterials;
lightingStruct* currentLighting;

void init()
{
	glClearColor(0.0,0.0,0.0,0.0);      //指定屏幕背景为黑色

	glEnable(GL_LIGHTING);       //启用光照
	glEnable(GL_LIGHT0);        //启用光源0

	glEnable(GL_DEPTH_TEST);

	currentMaterials=&redplasticMaterials;
	
	glMaterialfv(GL_FRONT,GL_AMBIENT,currentMaterials->ambient);
	glMaterialfv(GL_FRONT,GL_DIFFUSE,currentMaterials->diffuse);
	glMaterialfv(GL_FRONT,GL_SPECULAR,currentMaterials->specular);
	glMaterialfv(GL_FRONT,GL_SHININESS,&currentMaterials->shininess);

	currentLighting=&whiteLighting;

	glLightfv(GL_LIGHT0,GL_AMBIENT,currentLighting->ambient);
	glLightfv(GL_LIGHT0,GL_DIFFUSE,currentLighting->diffuse);
	glLightfv(GL_LIGHT0,GL_SPECULAR,currentLighting->specular);
	glLightfv(GL_LIGHT0,GL_POSITION,light_0pos);

	glEnable(GL_COLOR_MATERIAL);
}

void polygon(int* index)
{
	glBegin(GL_QUADS);
	for(int i=0;i<4;i++)
		glVertex3fv(vertices[index[i]]);
	glEnd();
}

void display()
{

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);         //清理屏幕颜色为我们指定的颜色
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glRotatef(theta[0],1.0,0.0,0.0);
	glRotatef(theta[1],0.0,1.0,0.0);
	glRotatef(theta[2],0.0,0.0,1.0);

	for(int i=0;i<6;i++)
	{
		glNormal3fv(normals[i]);
		polygon(index[i]);
	}
	glFlush();                       //强制以上绘图操作执行
}

void reshape(int w,int h)
{
	glMatrixMode(GL_PROJECTION);       //设置为投影模式
	glLoadIdentity();
	glOrtho(-2.0,2.0,-2.0,2.0,-2.0,2.0);

	glViewport(0,0,(GLsizei)w,(GLsizei)h);
}

void mouse(int button,int state,int x,int y)
{
	if (button==GLUT_LEFT_BUTTON && state==GLUT_DOWN)
	{
		axis=0;
	}
	if (button==GLUT_MIDDLE_BUTTON && state==GLUT_DOWN)
	{
		axis=1;
	}
	if (button==GLUT_RIGHT_BUTTON && state==GLUT_DOWN)
	{
		axis=2;
	}
}

void SpinIdle()
{
	theta[axis]+=0.1;
	if(theta[axis] >360.0)  theta[axis] -=360.0;
	glutPostRedisplay();
}

void key(unsigned char k,int x,int y)
{
	switch (k)
	{
	case '1':
		glutIdleFunc(NULL);
		break;
	case '2':
		glutIdleFunc(SpinIdle);
		break;
	case '3':
		currentMaterials=&redplasticMaterials;
		break;
	case '4':
		currentMaterials=&whiteShinyMaterials;
		break;
	case '5':
		currentMaterials=&brassMaterials;
		break;
	case '6':
		currentLighting=&whiteLighting;
		break;
	case '7':
		currentLighting=&coloredLighting;
		break;
	case 'q':
		exit(0);
		break;
	}
	
	glMaterialfv(GL_FRONT,GL_AMBIENT,currentMaterials->ambient);
	glMaterialfv(GL_FRONT,GL_DIFFUSE,currentMaterials->diffuse);
	glMaterialfv(GL_FRONT,GL_SPECULAR,currentMaterials->specular);
	glMaterialfv(GL_FRONT,GL_SHININESS,&currentMaterials->shininess);


	glLightfv(GL_LIGHT0,GL_AMBIENT,currentLighting->ambient);
	glLightfv(GL_LIGHT0,GL_DIFFUSE,currentLighting->diffuse);
	glLightfv(GL_LIGHT0,GL_SPECULAR,currentLighting->specular);

	glutPostRedisplay();
}

int main(int argc,char**argv)
{
	glutInit(&argc,argv);                           //初始化glut
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);    //设置窗口模式为单缓冲和RGB模式
	glutInitWindowSize(500,500);                    //设置窗口大小
	glutCreateWindow("test");                       //设置窗口标题
	glutDisplayFunc(display);                       //设置绘图回调函数
	glutReshapeFunc(reshape);                       //设置窗口回调函数
	glutMouseFunc(mouse);
	glutIdleFunc(SpinIdle);
	glutKeyboardFunc(key);
	init();
	glutMainLoop();                                 //开始循环,等待响应
	return 0;
}

运行可以得到如下结果:

    wKioL1QFPLiiucm1AAVfvr-spY4758.gif

对明暗计算的控制

    void glLightModel<if>(GLenum param,type value)

    void glLightModel<if>v(GLenum param,type value)

    //为param(GL_LIGHT_MODEL_AMBIENT,GL_LIGHT_MODEL_LOCAL_VIEWR,GL_LIGHT_MODEL_TWO_SIDE)设置光照模型。

    

   许多情况下,背面不需要计算光照,OpenGL能够利用这种情况,不对背面进行任何的光照计算。如果确实需要两面的光照计算,则可以如下设置:

    void glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE)


    如果视点距离物体远,则当我们移动物体时,从物体上任一点指向视点的向量几乎没什么变化。所以,可以通知OpenGL视点与场景的距离无穷远:

    glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWR,GL_TRUE)


    若果所有的光源都禁用,环境光将不复存在。但是,我们仍希望少量的环境光存在。则可以设置一个全局环境光源来达到效果:

    glLightModeli(GL_LIGHT_MODEL_AMBIENT,global_ambient)


平滑着色

    glShadeModel()   //参数可设为GL_SMOOTH或GL_FLAT


法线的处理

    光照计算要求法向量为单位向量,我们可以开启自动向量规范化:

    glEnable(GL_NORMALIZE)