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,¤tMaterials->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,¤tMaterials->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; }
运行可以得到如下结果:
对明暗计算的控制
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()